<!DOCTYPE html>
<html lang="cn" dir="auto">

<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="index, follow">
<title>Spring 循环依赖 | 染竹君的个人博客</title>
<meta name="keywords" content="Java, Spring, 循环依赖">
<meta name="description" content="Spring 中解决循环依赖问题的源码剖析。">
<meta name="author" content="SadBird">
<link rel="canonical" href="https://www.liyangjie.cn/posts/work/spring-circular-dependency/">
<link crossorigin="anonymous" href="/assets/css/stylesheet.min.4b7d810bc0c98044b1c7a72962d55d7f125a07c3ed5cf31e670c3fdecc98b341.css" integrity="sha256-S32BC8DJgESxx6cpYtVdfxJaB8PtXPMeZww/3syYs0E=" rel="preload stylesheet" as="style">
<link rel="icon" href="https://www.liyangjie.cn/favicon/favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="https://www.liyangjie.cn/favicon/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://www.liyangjie.cn/favicon/favicon-32x32.png">
<link rel="apple-touch-icon" href="https://www.liyangjie.cn/favicon/apple-touch-icon.png">
<link rel="mask-icon" href="https://www.liyangjie.cn/favicon/safari-pinned-tab.svg">
<meta name="theme-color" content="#2e2e33">
<meta name="msapplication-TileColor" content="#2e2e33">
<noscript>
    <style>
        #theme-toggle,
        .top-link {
            display: none;
        }

    </style>
    <style>
        @media (prefers-color-scheme: dark) {
            :root {
                --theme: rgb(29, 30, 32);
                --entry: rgb(46, 46, 51);
                --primary: rgb(218, 218, 219);
                --secondary: rgb(155, 156, 157);
                --tertiary: rgb(65, 66, 68);
                --content: rgb(196, 196, 197);
                --hljs-bg: rgb(46, 46, 51);
                --code-bg: rgb(55, 56, 62);
                --border: rgb(51, 51, 51);
            }

            .list {
                background: var(--theme);
            }

            .list:not(.dark)::-webkit-scrollbar-track {
                background: 0 0;
            }

            .list:not(.dark)::-webkit-scrollbar-thumb {
                border-color: var(--theme);
            }
        }

    </style>
</noscript>
<meta name="baidu-site-verification" content="code-9oLyeix0aK" />
<script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?4a41bf85d719f0e8c3165fc76904f546";
      var s = document.getElementsByTagName("script")[0]; 
      s.parentNode.insertBefore(hm, s);
    })();
</script>


<script async src="https://www.googletagmanager.com/gtag/js?id=G-C6GDZ56F4S"></script>
<script>
var doNotTrack = false;
if (!doNotTrack) {
	window.dataLayer = window.dataLayer || [];
	function gtag(){dataLayer.push(arguments);}
	gtag('js', new Date());
	gtag('config', 'G-C6GDZ56F4S', { 'anonymize_ip': false });
}
</script>
<meta property="og:title" content="Spring 循环依赖" />
<meta property="og:description" content="Spring 中解决循环依赖问题的源码剖析。" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://www.liyangjie.cn/posts/work/spring-circular-dependency/" />
<meta property="og:image" content="https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg" /><meta property="article:section" content="posts" />
<meta property="article:published_time" content="2022-01-27T16:50:18&#43;08:00" />
<meta property="article:modified_time" content="2022-01-27T16:50:18&#43;08:00" />

<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg" />
<meta name="twitter:title" content="Spring 循环依赖"/>
<meta name="twitter:description" content="Spring 中解决循环依赖问题的源码剖析。"/>


<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position":  1 ,
      "name": "Posts",
      "item": "https://www.liyangjie.cn/posts/"
    }, 
    {
      "@type": "ListItem",
      "position":  2 ,
      "name": "Work",
      "item": "https://www.liyangjie.cn/posts/work/"
    }, 
    {
      "@type": "ListItem",
      "position":  3 ,
      "name": "Spring 循环依赖",
      "item": "https://www.liyangjie.cn/posts/work/spring-circular-dependency/"
    }
  ]
}
</script>
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "Spring 循环依赖",
  "name": "Spring 循环依赖",
  "description": "Spring 中解决循环依赖问题的源码剖析。",
  "keywords": [
    "Java", "Spring", "循环依赖"
  ],
  "articleBody": "什么是循环依赖 国际惯例，还是直接先上一段官方的原文描述：\nCircular dependencies\r\rIf you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.\nFor example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.\nOne possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.\nUnlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).\n\r\r Spring 中如果使用构造器的方式进行注入，那么有可能出现无法解决的循环依赖问题，举例来说：A 类中使用构造器的方式注入 B，B 类同样使用构造器方式注入 A，那么当 Spring IoC 容器检测到这种循环相互引用关系时，就会抛出运行时异常 BeanCurrentlyInCreationException。\n\r\r解决方式也比较简单，使用 setter 方式替代构造器方式进行依赖注入。这种方式区别于构造器注入，A 和 B 之间的循环依赖迫使 A、B 之中的某一个对象在被「完全初始化之前」（已经完成「实例化」，内存中存在已经存在该对象及其引用）被注入到另一个对象中，从而不会由于这种特殊关系导致运行时异常。这里的描述可能有点绕，什么叫「完全初始化之前」，什么又是「实例化」，接下来将用一个简单的示例进行解释。\n\r\r 简单示例及源码剖析 构造器注入方式 示例包结构如下：\n1 2 3 4  circular ├─A.java ├─B.java └─CircularReferenceDemo.java   A 和 B 代码如下：\n1 2 3 4 5 6 7 8 9 10  @Component public class A { public A(B b) {} } @Component public class B { public B(A a) {} }   主程序 CircularReferenceDemo 代码如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17  @ComponentScan public class CircularReferenceDemo { public static void main(String[] args) { // 注解方式进行测试，需要使用 AnnotationConfigApplicationContext  AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册当前 CircularReferenceDemo，以触发 @ComponentScan 扫描当前包下的 A 类和 B 类  context.register(CircularReferenceDemo.class); // 启动容器  context.refresh(); // 获取 bean 实例  A a = context.getBean(A.class); B b = context.getBean(B.class); } }   运行程序后，代码高亮部分抛出异常如下：\n循环依赖异常\r\rCaused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘b’ defined in file [B.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘a’: Requested bean is currently in creation: Is there an unresolvable circular reference?\r\r 英语不好的同学直接看最后一句话，Is there an unresolvable circular reference?，Spring 提示您：是否存在未解决的循环依赖？Of course！\n那么，接下来就要进入源码进行分析了。\n还是再来一段 Spring 官方文档的描述：\n正常 setter 依赖注入的过程\r\rIf no circular dependencies exist, when one or more collaborating beans are being injected into a dependent bean, each collaborating bean is totally configured prior to being injected into the dependent bean. This means that, if bean A has a dependency on bean B, the Spring IoC container completely configures bean B prior to invoking the setter method on bean A. In other words, the bean is instantiated (if it is not a pre-instantiated singleton), its dependencies are set, and the relevant lifecycle methods (such as a configured init method or the InitializingBean callback method) are invoked.\r\r 可以看到，在 setter 依赖且无循环依赖的情况下，如果 A 依赖于 B，那么 Spring 容器默认会率先完成 B 的所有创建和初始化过程，随后再进行 A 中 setter 方法的调用。同理，使用构造器进行依赖注入时，也会先触发 B 的创建和初始化，再将已经完成所有过程的 B 对象注入到 A 中，完成 A 的创建工作。\n因此，这里将循环依赖分为三个过程：\n A 对象开始获取 getBean(A)，容器标记 A 对象正在创建中，准备构造器，发现构造器中有对于 B 的依赖，于是转向 B 的获取。 B 对象开始获取 getBean(B)，容器标记 B 对象正在创建中，准备构造器，发现构造器中有对于 A 的依赖，于是又准备开始 A 的获取。 再次进行 A 对象获取 getBean(A)，但发现 A 被标记为正在创建中，冲突，抛出异常。  下图为上述过程的整个调用栈，从下到上分别对应上面的步骤：\n\r\r将上图进行简化，提取一下其中的关键步骤，绘制流程图如下：\n\r\r最后来看一下标记某对象正在创建中的代码，getSingleton(beanName, singletonFactory) 方法中调用了 beforeSingletonCreation(beanName) 方法，该方法也比较简单，直接使用 Set 进行重复 Bean 创建的判断，如下:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21  /** Names of beans that are currently in creation. */ private final SetString singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16)); /** Names of beans currently excluded from in creation checks. */ private final SetString inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap(16)); // ...  /** * Callback before singleton creation. * The default implementation register the singleton as currently in creation. * @param beanName the name of the singleton about to be created * @see #isSingletonCurrentlyInCreation */ protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) \u0026\u0026 !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }    setter 注入方式 主程序 CircularReferenceDemo 保持不变，将 A、B 修改如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19  @Component public class A { private B b; @Autowired public void setB(B b) { this.b = b; } } @Component public class B { private A a; @Autowired public void setA(A a) { this.a = a; } }   发现可以正常通过运行而没有异常，说明循环依赖得到了解决，下面结合源码进行分析。\n复习一下上面提到过的一个基本思路：在 setter 依赖且无循环依赖的情况下，如果 A 依赖于 B，那么 Spring 容器默认会率先完成 B 的所有创建和初始化过程，随后再进行 A 中 setter 方法的调用。\n因此，先仿照构造器注入的方式给出一个猜测性的粗略流程：\n A 对象开始获取 getBean(A)，容器标记 A 对象正在创建中，使用默认构造器创建 A 对象（半成品 A’），创建完成后开始填充属性（setter 方法），发现有 B 的依赖，转向 B 的获取。 B 对象开始获取 getBean(B)，容器标记 B 对象正在创建中，使用默认构造器创建 B 对象（半成品 B’），创建完成后开始填充属性（setter 方法），发现有 A 的依赖，转向 A 的获取。 再次进行 A 对象获取 getBean(A)，在某个缓存中发现了 A 的半成品实例 A’（注意这里的 A’ 与最终要返回的 A 在不考虑代理的情况下是同一个对象，只是它当前处于一种中间的临时状态，还没有完成 B 属性填充，且其自身初始化方法还未调用），直接使用这个半成品 A’ 完成 B 的属性填充，最终完成 A 的属性填充。  \r\r关键的地方就是在 A 还未完全初始化完成时，提前「暴露」一个临时的半成品 A’ 给 B 进行依赖注入。暂时不太理解也没关系，后面会详细介绍这个缓存机制。\n惯例先来一段相关调用栈：\n\r\r同样可以发现分成了三个部分，其中很多方法与构造器方式一致，但它却解决了循环依赖的问题，因此需要更加细致地分析。\n现在来详细地介绍一下 Spring 容器启动时，预创建非懒加载 Bean 的具体流程及 Spring 容器的几个重要缓存，涉及到的类较多（画过一个时序图，Mermaid 格式，比较完整，放到 gist 上了）。\n下图是当前示例的 A、B 对象创建过程（看不清建议右键访问原图）：\n\r\r其中，A 和 B 对象的创建流程基本一致，思路都是先尝试从缓存中获取，若缓存中不存在，则进行创建。创建的核心步骤在 doCreateBean 方法中，主要包括四个过程：\n 创建对象实例 createBeanInstance：工厂方法、实例工厂方法、Supplier 或构造器等，本例中为默认构造器。这个方法调用之后，其实内存中已经创建好了一个 Bean 的实例对象，只不过是半成品，还没有调用 populateBean 完成属性填充，也未调用 initializeBean 完成初始化工作，这个状态的对象就是前文提到的「实例化」完成，但未进行初始化。 提前暴露半成品 addSingletonFactory：将上个步骤的半成品通过特殊包装放入缓存中，以解决循环依赖问题。 填充属性 populateBean：主要完成完成属性的填充、 @Autowired 的注入工作，从上一个调用栈图能看出调用的是 AutowiredAnnotationBeanPostProcessor 中的 postProcessProperties 方法完成注入。提前暴露方法 addSingletonFactory 在本方法之前调用也正是因为在 populate 过程中会触发依赖对象的获取过程，要在该过程前把当前的半成品放入缓存中。完成这个步骤的对象暂且称之为「属性填充完成」，但依然还未进行初始化。 自定义初始化 initializeBean：包括了 Aware 接口处理、BeanPostPostProcessor 的 before 和 after 逻辑、@PostConstruct 注解处理、init-method 处理、InitializingBean 处理等，注意在创建 A 的过程中，这个方法由于 populate 触发了依赖处理，因此将在 B 创建完成后才会被调用。这个方法执行完成后，我们才能把最终返回的对象称为「完全初始化」的对象，也就是所谓的成品。  Spring 容器中的缓存位于 DefaultSingletonBeanRegistry 类中，具体定义如下：\n1 2 3 4 5 6 7 8  /** Cache of singleton objects: bean name to bean instance. */ private final MapString, Object singletonObjects = new ConcurrentHashMap(256); /** Cache of singleton factories: bean name to ObjectFactory. */ private final MapString, ObjectFactory singletonFactories = new HashMap(16); /** Cache of early singleton objects: bean name to bean instance. */ private final MapString, Object earlySingletonObjects = new ConcurrentHashMap(16);   从名称及注释中能够获知它们分别的作用：\n singletonObjects：保存已经完成所有创建过程（包括所有初始化过程）的单例 Bean，即所有的成品 Bean 都会被放到这个缓存中。 singletonFactories：保存创建所有单例 Bean 的 ObjectFactory 对象，与代理有关，后续会详细介绍。 earlySingletonObjects：保存实例化完成，但未进行 populate 和 initializeBean 的半成品 Bean。  再回到创建流程图，橙色编号部分为缓存相关的方法，下面按编号顺序进行解析。\n  A 对象获取过程的第一个 getSingleton 方法源码如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25  protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Quick check for existing instance without full singleton lock  Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null \u0026\u0026 isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null \u0026\u0026 allowEarlyReference) { synchronized (this.singletonObjects) { // Consistent creation of early reference within full singleton lock  singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { ObjectFactory singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } } } return singletonObject; }   首先从 singletonObjects 缓存中获取，没有获取到，且此时的 isSingletonCurrentlyInCreation 方法返回为 false（还没有对 A 进行标记），会直接返回 null。\n假定 isSingletonCurrentlyInCreation 为 true，再从 earlySingletonObjects 中尝试获取，若仍为空且 allowEarlyReference 为 true（默认值），则加锁以保证后续操作的强一致性。对单例设计模式有了解的朋友这里肯定很熟悉，类似于 DCL 的操作。最后，尝试从 singletonFactories 中获取对象，因为我们这里是第一次进入，因此所有缓存都为空，最终返回 null。\n  缓存中不存在任何 A 对象，因此进入编号为 2 的方法 getSingleton()（注意区别上面那个 getSingleton 方法），这个方法有两个参数，第一个参数为 beanName，第二个参数为 ObjectFactory，Spring 源码中给出的调用参数如下：\n1 2 3 4 5 6 7 8 9 10 11 12  sharedInstance = getSingleton(beanName, () - { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there  // eagerly by the creation process, to allow for circular reference resolution.  // Also remove any beans that received a temporary reference to the bean.  destroySingleton(beanName); throw ex; } });   第二个参数使用了 lambda 表达式，类型推断为 ObjectFactory，因此，在 getSingleton 这个方法中一定会调用 ObjectFactory 的 getObject 方法，如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52  public Object getSingleton(String beanName, ObjectFactory singletonFactory) { Assert.notNull(beanName, \"Bean name must not be null\"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, \"Singleton bean creation not allowed while singletons of this factory are in destruction \" + \"(Do not request a bean from a BeanFactory in a destroy method implementation!)\"); } if (logger.isDebugEnabled()) { logger.debug(\"Creating shared instance of singleton bean '\" + beanName + \"'\"); } beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet(); } try { singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -  // if yes, proceed with it since the exception indicates that state.  singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } if (newSingleton) { addSingleton(beanName, singletonObject); } } return singletonObject; } }   重点关注高亮几行：\n beforeSingletonCreation 在构造器方式中以及介绍过了，通过 Set 对正在创建的 Bean 进行标记。 singletonObject = singletonFactory.getObject(); 果然调用了 ObjectFactory 的 getObject 方法，因此会进入 createBean 方法，稍后介绍。 afterSingletonCreation 方法作用与 beforeSingletonCreation 类似，在上一步 createBean 创建完成后，将正在创建的标记清除。 addSingleton 这个方法是一个重要方法，它将创建完成的 Bean 放入 singletonObjects 缓存中，并从 earlySingletonObjects 和 singletonFactories 中移除相应的同名临时对象。稍后在 8、9 中会详细介绍。    到目前为止，缓存中的仍然没有任何对象。进入到 addSingletonFactory 之前，在 doCreateBean 中会先调用在构造器方式中介绍过的 createBeanInstance 方法，该方法会选择适当的方式（本例中是默认构造器）将目标对象进行实例化（具体定位到 SimpleInstantiationStrategy 中的 instantiate 方法，再调用BeanUtils.instantiateClass(constructorToUse)，继续跟进，会找到 ctor.newInstance(argsWithDefaultValues)，即反射方式创建实例）。\n此时，本例中的 A 对象已经创建，但没有进行属性填充、初始化，是半成品对象，接下来就将这个半成品对象通过下面代码放入缓存中：\n1 2 3 4 5 6 7 8 9 10 11  // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() \u0026\u0026 this.allowCircularReferences \u0026\u0026 isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace(\"Eagerly caching bean '\" + beanName + \"' to allow for resolving potential circular references\"); } addSingletonFactory(beanName, () - getEarlyBeanReference(beanName, mbd, bean)); }   addSingletonFactory 方法第二个参数同样为 ObjectFactory，具体内容也比较简单：\n1 2 3 4 5 6 7 8 9 10  protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) { Assert.notNull(singletonFactory, \"Singleton factory must not be null\"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }   几个简单的操作相信都能看懂，该方法执行完成后，singletonFactories 缓存中出现了数据，三个 Map 的状态如下：\n\r\rA 对象暂时还没创建完成，接下来进入 populateBean 方法进行属性填充，该方法会触发 B 对象的获取，因此，会再次进入 getSingleton 方法以获取 B。\n  此次进入 getSingleton 目的是获取 B 对象，由于 B 对象在 singletonObjects 中不存在，且 isSingletonCurrentlyInCreation 返回为 false，因此直接返回 null。\n  参考 2 过程。\n  参考 3 过程，完成后，此时三个 Map 的状态更新为：\n\r\r同样,这里由于 B 的 @Autowired 依赖了 A，因此，会进入 getSingleton 再次获取 A。\n  注意，本次的 getSingleton 方法调用将与第一次有所不同，此时，虽然 singletonObjects 仍然没有对象，但 isSingletonCurrentlyInCreation 的返回值为 true。因此会进入到加锁块中：\n1 2 3 4 5 6  ObjectFactory singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); }   表面意思也比较简单，调用 singletonFactories 中 a 对应的 ObjectFactory 的 getObject 方法，获取半成品对象，并将该对象放入到 earlySingletonObjects 中，最后将该 ObjectFactory 从 singletonFactories 中移除。此时，三个 Map 的状态为：\n\r\r还需要关注这里高亮行的代码，还记得这里的 singletonFactory 是什么东西吗？在 3 过程中，addSingletonFactory(beanName, () - getEarlyBeanReference(beanName, mbd, bean)); 将 lambda 表达式存入了 singletonFactories，因此这里调用的 getObject 实际上就是调用  getEarlyBeanReference(beanName, mbd, bean))，它的代码如下：\n1 2 3 4 5 6 7 8 9  protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() \u0026\u0026 hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject; }   这里的过程是要调用 SmartInstantiationAwareBeanPostProcessor 中的 getEarlyBeanReference 方法，将原始的半成品实例进行包装，典型的应用就是在这里给 AOP 提供一个包装的机会，试想如果我们示例中的 A 对象需要被代理，那么在这里就会给 A 对象的半成品 A’ 进行包装，让等待依赖注入的 B 最终能够得到的是 AOP 包装后的 A，而不是原始的 A 对象。但本例中的 A 不需要代理，因此直接返回 A’ 对象即可。\n既然已经有了一个 A’ 对象，那么就满足了 B 的需求，随后返回到 B 的 populateBean 方法，直接将 A’ 注入到 B 对象中，完成 B 的属性填充，并调用 initializeBean 对 B 进行初始化。\n  这时 B 对象已经走完了整个创建的过程，是一个成品对象，要将它放入到成品缓存 singletonObjects 中。代码将返回到 5 过程，具体代码位置参考 2 过程中的 getSingleton 第 21 行高亮处。接下来将要调用 afterSingletonCreation 完成清理工作，将 B 对象的正在创建状态移除。但更重要的是 addSingleton(beanName, singletonObject) 方法：\n1 2 3 4 5 6 7 8  protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }   它将创建完成的 B 对象放入 singletonObjects 中，并从 singletonFactories 和 earlySingletonObjects 中移除同名的临时对象。至此，B 对象创建完成。\n\r\r同样道理，既然 B 已经完成了创建，就会返回到 A 的 populateBean 方法，将 B 属性注入 A，并调用 initializeBean 对 A 进行初始化。\n  同过程 8，此时要将完成创建工作的 A 对象放入 singletonObjects。至此，A 对象也创建完成，循环依赖得到了解决。\n\r\r   AOP 和循环依赖 AOP 是 Spring 的核心功能之一，而 Spring 中的 AOP 实现手段主要是「动态代理」，无论是 JDK 亦或是 CGLIB，都是针对方法层面进行增强。本节将首先介绍 AOP 的核心概念及部分 API，然后再对 AOP 代理对象间的循环依赖进行分析。\nAOP 介绍 简单来说，Spring 中的 AOP 就是向外提供一个「代理对象」，该对象中封装了所有适配的拦截器和原始对象的目标方法，成为一个调用链，在调用代理对象的方法时处理调用链中的所有逻辑（包括目标方法本身），以达到动态增强的效果，顺带一提，Spring 中「代理对象」主要有两种实现：JdkDynamicAopProxy 和 CglibAopProxy，它们的 getProxy 方法可以返回我们所需的「代理对象」，感兴趣的可以大致浏览下源码，核心部分都是拦截器链的封装和调用。\n这里普及一下 AOP 中一些 API，涉及了部分源码。实际工作中，我们通常都是使用 @Aspect 注解的方式来声明切面类（包含 @Pointcut 切点描述及 @Before、@After 等通知），但实际上 Spring 提供了一套清晰可用的编程接口，可以灵活地进行定制实现。下面先介绍一些基本的接口：\n  JoinPoint：AOP 中的连接点，在 Spring 只支持方法层面的增强，这点从它的唯一子接口 MethodInvacation 中也可以看出，因此直接理解为方法即可。\n  Pointcut：AOP 中的切点，作用是从所有的连接点中进行筛选操作，它不具备任何的具体业务逻辑，仅仅是选出我们所需要增强的方法。\n  Advice：AOP 中的通知，它是具体增强业务的实现，Spring 中支持 before、after、afterReturning、afterThrows 和 around 这几个类型的通知，它协同 Pointcut 在特定的方法上在特定「位置」实现增强操作。\n  Advisor：Spring 中的一个中间接口，主要使用其子接口 PointcutAdivsor，它将 Pointcut 和 Advice 进行组合，放在一个 Advisor 类中，方便使用。\n  MethodInterceptor：字面意思是方法拦截器，它是 Advice 的子接口，Spirng 中的 Around 通知就是使用它来实现的，且所有的其他通知都会交由相应适配器处理，最终都转换为 MethodInterceptor 放入「代理对象」的拦截器链中。\n  ProxyFactory：创建「代理对象」的工厂类，它是 AdvisedSupport 的子类，提供了一系列配置的方法，内部使用 DefaultAopProxyFactory 创建具体的「代理对象」，DefaultAopProxyFactory 核心代码如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (!NativeDetector.inNativeImage() \u0026\u0026 (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) { Class targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException(\"TargetSource cannot determine target class: \" + \"Either an interface or a target is required for proxy creation.\"); } if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { // JDK 动态代理  return new JdkDynamicAopProxy(config); } // CGLIB 动态代理，ObjenesisCglibAopProxy 是 CglibAopProxy 的子类  return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }     AbstractAutoProxyCreator：与 Spring IOC 结合，实现 Spring 容器 AOP 代理对象的自动创建功能，本质上它是SmartInstantiationAwareBeanPostProcessor，实现了以下几个重要的逻辑：\n postProcessBeforeInstantiation：在 Bean 实例化前被调用，调用了 isInfrustructureClass 和 shouldSkip 来决定当前 Bean 是否应该被代理，其中 shouldSkip 会触发 findEligibleAdvisors 的逻辑，将容器中所有切面逻辑进行筛选。同时包含了处理自定义 TargetSource 的业务（不常用，具体可以查看官方文档，与热替换和池化技术关联较大）。 getEarlyReference：如果当前 Bean 需要被代理，则在解决循环依赖的过程中，需要提前暴露这个「代理对象」，以替换原对象，成为注入到其他 Bean 中的真正对象。核心方法 wrapIfNecessary。 postProcessAfterInitialization：在初始化后调用，属于 BeanPostProcessor 接口中的方法，同样调用了 wrapIfNecessary 方法，在对象创建完成且属性填充完成后进行生成代理操作。    有了上述这些基本概念后，下面是两个具体的实例，分别展示了 Spring 手动创建代理和自动创建代理：\n  手动创建\n目标类：\n1 2 3 4 5 6 7 8 9  public class ManualA { public void execute() { System.out.println(\"Executing A...\"); } public void other() { System.out.println(\"Other method...\"); } }   实现手动创建代理：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59  public class ManualProxyDemo { static class ManualMethodInterceptor implements MethodInterceptor { @Nullable @Override public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable { System.out.println(\"---------------------\"); System.out.println(\"Enhancement Before...\"); Object result = invocation.proceed(); System.out.println(\"Enhancement After...\"); System.out.println(\"---------------------\"); return result; } } public Advice methodInterceptor() { return new ManualMethodInterceptor(); } public Pointcut manualPointcut() { return new StaticMethodMatcherPointcut() { @Override public boolean matches(Method method, Class targetClass) { // 切点，只匹配方法名为 execute 的方法  return \"execute\".equals(method.getName()); } }; } public Advisor manualAdvisor(Pointcut pointcut, Advice methodInterceptor) { return new DefaultPointcutAdvisor(pointcut, methodInterceptor); } public static void main(String[] args) { ManualProxyDemo config = new ManualProxyDemo(); // Pointcut、Advice、Advisor 准备  Pointcut pointcut = config.manualPointcut(); Advice advice = config.methodInterceptor(); Advisor advisor = config.manualAdvisor(pointcut, advice); // 目标对象  ManualA target = new ManualA(); // 代理工厂，设置目标对象  ProxyFactory proxyFactory = new ProxyFactory(target); // 配置  // 使用 CGLIB 动态代理  proxyFactory.setProxyTargetClass(true); // 将我们准备好的 Advisor 设置到工厂中  proxyFactory.addAdvisor(advisor); // 创建「代理对象」  ManualA proxy = (ManualA) proxyFactory.getProxy(); // 使用「代理对象」执行符合 Pointcut 的目标方法  proxy.execute(); // 使用「代理对象」执行其他不符合的方法  proxy.other(); } }   手动创建代理的流程也比较简单，准备好 Pointcut、Advice 对象，并封装为 Advisor 添加到 ProxyFactory中（也可以直接添加 Advice，工厂会自动进行封装），ProxyFactory 可在构造函数中指定需要代理的目标对象，且可以设置一些通用属性（如 proxyTargetClass、exposeProxy 等）。最终调用 getProxy 方法获取我们所需要的「代理对象」。执行上面代码后，结果如下：符合 Pointcut 的方法 execute 得到了增强，而 other 方法则直接调用原始对象中的同名方法。\n1 2 3 4 5 6  --------------------- Enhancement Before... Executing A... Enhancement After... --------------------- Other method...     自动创建\n目标类中新增一个注解和内部方法调用：\n1 2 3 4 5 6 7 8 9 10 11  @Component public class ManualA { public void execute() { System.out.println(\"Executing A...\"); } public void other() { execute(); System.out.println(\"Other method...\"); } }   自动创建代理，新增 @EnableAspectJAutoProxy 注解和 @Bean、@Primary，移除手动创建的代码，取而代之的是使用 Spring IOC 自动创建的方式：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59  @ComponentScan @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) public class ManualProxyDemo { static class ManualMethodInterceptor implements MethodInterceptor { @Nullable @Override public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable { System.out.println(\"---------------------\"); System.out.println(\"Enhancement Before...\"); Object result = invocation.proceed(); System.out.println(\"Enhancement After...\"); System.out.println(\"---------------------\"); return result; } } // @Bean 注解将 MethodInterceptor 交由 Spring 管理，@Primary 防止其他 MethodInterceptor 干扰  @Bean @Primary public Advice methodInterceptor() { return new ManualMethodInterceptor(); } @Bean @Primary public Pointcut manualPointcut() { return new StaticMethodMatcherPointcut() { @Override public boolean matches(Method method, Class targetClass) { // 切点，只匹配方法名为 execute 的方法  return \"execute\".equals(method.getName()); } }; } @Bean @Primary public Advisor manualAdvisor(Pointcut pointcut, Advice methodInterceptor) { return new DefaultPointcutAdvisor(pointcut, methodInterceptor); } public static void main(String[] args) { // 结合 Spring 容器使用  AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(ManualProxyDemo.class); // 启动容器，过程中创建 Bean 实例，并完成「代理对象」的自动创建  context.refresh(); // 从容器中获取对象，该对象为我们需要的「代理对象」  ManualA proxy = context.getBean(ManualA.class); // 打印 proxy 的类名  System.out.println(proxy.getClass().getName()); // 执行结果与手动创建类似  proxy.execute(); // 注意这个方法，它内部调用了 execute 方法  proxy.other(); } }   与手动创建代理对象相比，首先需要使用 @EnableAspectJAutoProxy 将自动创建代理的相关类导入。具体来说，在 @EnableAspectJAutoProxy 类上可以找到 @Import(AspectJAutoProxyRegistrar.class)。进入 AspectJAutoProxyRegistrar 的 registerBeanDefinitions 方法及相关 AopConfigUtils 配置类，如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43  @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { if (enableAspectJAutoProxy.getBoolean(\"proxyTargetClass\")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean(\"exposeProxy\")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } @Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) { return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null); } @Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); } public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) { if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); definition.getPropertyValues().add(\"proxyTargetClass\", Boolean.TRUE); } } public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) { if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); definition.getPropertyValues().add(\"exposeProxy\", Boolean.TRUE); } }   重点部分已用高亮标出，主要是将 AnnotationAwareAspectJAutoProxyCreator 类作为 BeanDefinition 加入到容器中，且为其配置了 proxyTargetClass 属性和 exposeProxy 属性。proxyTargetClass 设置为 true，表示强制使用 CGLIB 动态代理；exposeProxy 设置为 true，表示需要暴露 ManualA 的「代理对象」，可以使用 AopContext 获取该对象。\n自动创建的方式将 Pointcut、Advice 及 Advisor 交由 Spring 容器管理，容器启动后，会自动根据我们配置好的 Advisor 为我们将原始的 ManualA 对象进行增加，创建其「代理对象」，并将该代理对象放入 singletonObjects 缓存中。调用 getBean 可以从容器的 singletonObjects 缓存中获取出 ManualA 类型的 Bean，通过 execute 和 other 的执行结果我们可以发现该对象是「代理对象」而非原始的目标对象。\n1 2 3 4 5 6 7 8 9  cn.liyangjie.spring.circular.ManualA$$EnhancerBySpringCGLIB$$d4361f08 --------------------- Enhancement Before... Executing A... Enhancement After... --------------------- Executing A... in other() Executing A... Other method...   同时需要注意到，上述 other 方法内部调用的 execute 并没有经过增强，只是简单的调用原始方法，这就是 exposeProxy 的作用所在了，为了让 other 能够调用到增强的 execute 方法，这点也适用于 Spring 事务中，同一个类中的 @Transactional 方法间的相互调用。\n现在将 ManualA 的 other 修改如下：\n1 2 3 4 5 6  public void other() { System.out.println(\"Executing A... in other()\"); ManualA proxy = (ManualA) AopContext.currentProxy(); proxy.execute(); System.out.println(\"Other method...\"); }   再执行得到的结果如下：other 中成功调用了增强后的 execute 方法。\n1 2 3 4 5 6 7 8 9 10 11 12 13  cn.liyangjie.spring.circular.ManualA$$EnhancerBySpringCGLIB$$d4361f08 --------------------- Enhancement Before... Executing A... Enhancement After... --------------------- Executing A... in other() --------------------- Enhancement Before... Executing A... Enhancement After... --------------------- Other method...      AOP 下的循环依赖 现在我们使用示例代码如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13  @Component public class ServiceA { private ServiceB serviceB; @Autowired public void setServiceB(ServiceB serviceB) { this.serviceB = serviceB; } public void service() { System.out.println(\"Executing Service A...\"); } }   1 2 3 4 5 6 7 8 9 10 11 12 13  @Component public class ServiceB { private ServiceA serviceA; @Autowired public void setServiceA(ServiceA serviceA) { this.serviceA = serviceA; } public void service() { System.out.println(\"Executing ServiceB...\"); } }   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48  @ComponentScan // 使用 CGLIB 动态代理，并且暴露「代理对象」，可使用 AopContext 访问 @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) public class CircularReferenceDemo { static class CircularInterceptor implements MethodInterceptor { @Nullable @Override public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable { System.out.println(\"Enhancement...\"); return null; } } @Bean public MethodInterceptor interceptor() { return new CircularInterceptor(); } @Bean public Pointcut pointcut() { return new StaticMethodMatcherPointcut(){ @Override public boolean matches(Method method, Class targetClass) { return \"service\".equals(method.getName()); } }; } @Bean public Advisor advisor() { return new DefaultPointcutAdvisor(pointcut(), interceptor()); } public static void main(String[] args) { // 注解方式进行测试，需要使用 AnnotationConfigApplicationContext  AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册当前 CircularReferenceDemo，以触发 @ComponentScan 扫描当前包  context.register(CircularReferenceDemo.class); // 启动容器  context.refresh(); // 获取 bean 实例  ServiceA a = context.getBean(ServiceA.class); ServiceB b = context.getBean(ServiceB.class); } }   ServiceA 和 ServiceB 存在循环依赖关系，且这两个对象都有 service 方法，满足 AOP 自动代理的条件，这就是典型的 AOP 场景下的循环依赖。\n从之前的 AOP 示例中我们可以了解到，对于「代理对象」和原始目标对象，Spring 容器中对外暴露的是前者，因此，在依赖注入的过程中，循环依赖要注入的也是 ServiceA 和 ServiceB 的代理对象。\n有了上面 setter 方式的分析，这里偷个懒，还是使用上面那张图，图中的 A 和 B 请读者自动替换为 ServiceA 和 ServiceB。在图中标出了 3 个紫色的位置，这是接下来要分析的重点。\n\r\r流程还是和 setter 方式类似，从 ServiceA 进入，再到 ServiceA 中的 populate 触发 ServiceB 的获取。而 ServiceB 的 populate 中同样又需要 ServiceA，因此还是会进入到步骤 7。\n如果有些记忆模糊的话，请回头去看看步骤 7 的具体流程，这里重新贴一下这段代码：\n1 2 3 4 5 6 7 8 9  protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() \u0026\u0026 hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject; }   刚才介绍过，当我们使用 @EnableAspectJAutoProxy 开启 AOP 自动创建模式后，容器中会导入一个 AbstractAutoProxyCreator 类，这个类此时满足这里的 SmartInstantiationAwareBeanPostProcessor 条件，因此会调用 exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); 对我们的原始 ServiceA 的半成品进行增强处理。AbstractAutoProxyCreator 中 getEarlyBeanReference 具体代码如下：\n1 2 3 4 5 6  @Override public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); }   这里就不深入展开了，只需要了解到 wrapIfNecessary 是具体创建半成品「代理对象」的位置就行了。\n步骤 7 完成后，由于有了 ServiceA 半成品「代理对象」，此时就可以返回 ServiceB 的 populate 方法，并完成其属性填充工作。随后调用 initializeBean 方法，调用该方法前，ServiceB 还是原始的目标类型，没有任何增强。\ninitializeBean 中在执行了各种初始化操作后，会进入 BeanPostProcessor 的 after 中，AbstractAutoProxyCreator 中该方法的实现如下：\n1 2 3 4 5 6 7 8 9 10  @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }   是不是感觉非常得熟悉呢？这里同样调用了 wrapIfNecessary 方法，完成了 ServiceB 成品「代理对象」创建，注意，这里是成品。\n接下来，使用这个 ServiceB 的成品「代理对象」完成 ServiceA 的 populate 工作，并进入到 ServiceA 的 initializingBean 方法。这时与 ServiceB 中不同的是，ServiceA 已经创建过一次并且进行了相关缓存（cacheKey 的作用），因此不会再进行创建，直接返回即可。\n最终，容器中的 ServiceA 和 ServiceB 均顺利创建，都被进行了动态代理增强，且循环依赖也得到了解决。\n 总结 Spring 循环依赖分为两种情况：\n 构造器注入 setter注入  只有在 setter（包括 @Autowired 注解，它的注入时期也是在 populate 中）注入情景下才能够解决，整体思路是利用提前「暴露」一个半成品对象完成其中一方的属性填充。而 AOP 场景下的循环依赖实际上还是依赖这套逻辑，只是对提前「暴露」的这个半成品进行了一些处理。\n",
  "wordCount" : "3301",
  "inLanguage": "cn",
  "image":"https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg","datePublished": "2022-01-27T16:50:18+08:00",
  "dateModified": "2022-01-27T16:50:18+08:00",
  "author":[{
    "@type": "Person",
    "name": "SadBird"
  }],
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://www.liyangjie.cn/posts/work/spring-circular-dependency/"
  },
  "publisher": {
    "@type": "Organization",
    "name": "染竹君的个人博客",
    "logo": {
      "@type": "ImageObject",
      "url": "https://www.liyangjie.cn/favicon/favicon.ico"
    }
  }
}
</script>
</head>

<body class="">
    <a id="top"></a>
<script>
    if (localStorage.getItem("pref-theme") === "dark") {
        document.body.classList.add('dark');
    } else if (localStorage.getItem("pref-theme") === "light") {
        document.body.classList.remove('dark')
    } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
        document.body.classList.add('dark');
    }

</script>

<header class="header">
    <nav class="nav">
        <div class="logo">
            <a href="https://www.liyangjie.cn/" accesskey="h" title="染竹君的小站 (Alt + H)">
                        
                    <img src="https://www.liyangjie.cn/img/logo_hue7837a3d2f79ccdd94da0537755daebc_15738_0x30_resize_box_3.png" alt="logo" aria-label="logo"
                        height="30">染竹君的小站</a>
            <span class="logo-switches">
                <button id="theme-toggle" accesskey="t" title="(Alt + T)">
                    <svg id="moon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                        fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                        stroke-linejoin="round">
                        <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
                    </svg>
                    <svg id="sun" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                        fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                        stroke-linejoin="round">
                        <circle cx="12" cy="12" r="5"></circle>
                        <line x1="12" y1="1" x2="12" y2="3"></line>
                        <line x1="12" y1="21" x2="12" y2="23"></line>
                        <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
                        <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
                        <line x1="1" y1="12" x2="3" y2="12"></line>
                        <line x1="21" y1="12" x2="23" y2="12"></line>
                        <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
                        <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
                    </svg>
                </button>
            </span>
        </div>
        <ul id="menu">
            <li>
                <a href="https://www.liyangjie.cn/categories/" title="分类">
                    <span>分类</span>
                </a>
            </li>
            <li>
                <a href="https://www.liyangjie.cn/tags/" title="标签">
                    <span>标签</span>
                </a>
            </li>
            <li>
                <a href="https://www.liyangjie.cn/archives/" title="归档">
                    <span>归档</span>
                </a>
            </li>
            <li>
                <a href="https://www.liyangjie.cn/search/" title="搜索">
                    <span>搜索</span>
                </a>
            </li>
        </ul>
    </nav>
</header>
<main class="main">

<article class="post-single">
  <header class="post-header">
    <div class="breadcrumbs"><a href="https://www.liyangjie.cn/">Home</a>&nbsp;»&nbsp;<a href="https://www.liyangjie.cn/posts/">Posts</a>&nbsp;»&nbsp;<a href="https://www.liyangjie.cn/posts/work/">Work</a></div>
    <h1 class="post-title">
      Spring 循环依赖
    </h1>
    <div class="post-meta"><span title='2022-01-27 16:50:18 +0800 CST'>January 27, 2022</span>&nbsp;·&nbsp;16 min&nbsp;·&nbsp;SadBird&nbsp;|&nbsp;<a href="https://github.com/YazidLee/hugo-backup/tree/master/content/posts/work/Spring%20%e5%be%aa%e7%8e%af%e4%be%9d%e8%b5%96.md" rel="noopener noreferrer" target="_blank">Suggest Changes</a>
</div>
  </header> <figure class="entry-cover"><img class="lazy" src="/svg/loading.min.svg"  data-src="https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg" loading="lazy" alt="Spring 循环依赖">
        
</figure><div class="toc">
    <details >
        <summary accesskey="c" title="(Alt + C)">
            <span class="details">Table of Contents</span>
        </summary>

        <div class="inner"><ul>
                <li>
                    <a href="#%e4%bb%80%e4%b9%88%e6%98%af%e5%be%aa%e7%8e%af%e4%be%9d%e8%b5%96" aria-label="什么是循环依赖">什么是循环依赖</a></li>
                <li>
                    <a href="#%e7%ae%80%e5%8d%95%e7%a4%ba%e4%be%8b%e5%8f%8a%e6%ba%90%e7%a0%81%e5%89%96%e6%9e%90" aria-label="简单示例及源码剖析">简单示例及源码剖析</a><ul>
                        
                <li>
                    <a href="#%e6%9e%84%e9%80%a0%e5%99%a8%e6%b3%a8%e5%85%a5%e6%96%b9%e5%bc%8f" aria-label="构造器注入方式">构造器注入方式</a></li>
                <li>
                    <a href="#setter-%e6%b3%a8%e5%85%a5%e6%96%b9%e5%bc%8f" aria-label="setter 注入方式">setter 注入方式</a></li></ul>
                </li>
                <li>
                    <a href="#aop-%e5%92%8c%e5%be%aa%e7%8e%af%e4%be%9d%e8%b5%96" aria-label="AOP 和循环依赖">AOP 和循环依赖</a><ul>
                        
                <li>
                    <a href="#aop-%e4%bb%8b%e7%bb%8d" aria-label="AOP 介绍">AOP 介绍</a></li>
                <li>
                    <a href="#aop-%e4%b8%8b%e7%9a%84%e5%be%aa%e7%8e%af%e4%be%9d%e8%b5%96" aria-label="AOP 下的循环依赖">AOP 下的循环依赖</a></li></ul>
                </li>
                <li>
                    <a href="#%e6%80%bb%e7%bb%93" aria-label="总结">总结</a>
                </li>
            </ul>
        </div>
    </details>
</div>

  <div class="post-content"><h2 id="什么是循环依赖">什么是循环依赖<a hidden class="anchor" aria-hidden="true" href="#什么是循环依赖">#</a></h2>
<p>国际惯例，还是直接先上一段官方的原文描述：</p>
<div class="details admonition quote open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-quote-right fa-fw"></i>Circular dependencies<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content"><p>If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.</p>
<p>For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.</p>
<p>One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.</p>
<p>Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).</p>
</div>
        </div>
    </div>
<p>Spring 中如果使用构造器的方式进行注入，那么有可能出现无法解决的循环依赖问题，举例来说：A 类中使用构造器的方式注入 B，B 类同样使用构造器方式注入 A，那么当 Spring IoC 容器检测到这种循环相互引用关系时，就会抛出运行时异常 <code>BeanCurrentlyInCreationException</code>。</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/27/BfkyoIDxXdYth49.png" title="https://s2.loli.net/2022/01/27/BfkyoIDxXdYth49.png" data-thumbnail="https://s2.loli.net/2022/01/27/BfkyoIDxXdYth49.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/27/BfkyoIDxXdYth49.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/27/BfkyoIDxXdYth49.png"
            alt="https://s2.loli.net/2022/01/27/BfkyoIDxXdYth49.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>解决方式也比较简单，使用 setter 方式替代构造器方式进行依赖注入。这种方式区别于构造器注入，A 和 B 之间的循环依赖迫使 A、B 之中的某一个对象在被「完全初始化之前」（已经完成「实例化」，内存中存在已经存在该对象及其引用）被注入到另一个对象中，从而不会由于这种特殊关系导致运行时异常。这里的描述可能有点绕，什么叫「完全初始化之前」，什么又是「实例化」，接下来将用一个简单的示例进行解释。</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/27/PgahynDtbUiOZX4.png" title="https://s2.loli.net/2022/01/27/PgahynDtbUiOZX4.png" data-thumbnail="https://s2.loli.net/2022/01/27/PgahynDtbUiOZX4.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/27/PgahynDtbUiOZX4.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/27/PgahynDtbUiOZX4.png"
            alt="https://s2.loli.net/2022/01/27/PgahynDtbUiOZX4.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<hr>
<h2 id="简单示例及源码剖析">简单示例及源码剖析<a hidden class="anchor" aria-hidden="true" href="#简单示例及源码剖析">#</a></h2>
<h3 id="构造器注入方式">构造器注入方式<a hidden class="anchor" aria-hidden="true" href="#构造器注入方式">#</a></h3>
<p>示例包结构如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">circular
</span></span><span class="line"><span class="cl">├─A.java 
</span></span><span class="line"><span class="cl">├─B.java 
</span></span><span class="line"><span class="cl">└─CircularReferenceDemo.java 
</span></span></code></pre></td></tr></table>
</div>
</div><p>A 和 B 代码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Component</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">A</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="nf">A</span><span class="o">(</span><span class="n">B</span> <span class="n">b</span><span class="o">)</span> <span class="o">{}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@Component</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">B</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="nf">B</span><span class="o">(</span><span class="n">A</span> <span class="n">a</span><span class="o">)</span> <span class="o">{}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>主程序 <code>CircularReferenceDemo</code> 代码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="hl"><span class="lnt">11
</span></span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@ComponentScan</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CircularReferenceDemo</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 注解方式进行测试，需要使用 AnnotationConfigApplicationContext
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">AnnotationConfigApplicationContext</span> <span class="n">context</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AnnotationConfigApplicationContext</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 注册当前 CircularReferenceDemo，以触发 @ComponentScan 扫描当前包下的 A 类和 B 类
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">context</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">CircularReferenceDemo</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 启动容器
</span></span></span><span class="line hl"><span class="cl"><span class="c1"></span>        <span class="n">context</span><span class="o">.</span><span class="na">refresh</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 获取 bean 实例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">A</span> <span class="n">a</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="n">A</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">B</span> <span class="n">b</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="n">B</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>运行程序后，代码高亮部分抛出异常如下：</p>
<div class="details admonition bug open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-bug fa-fw"></i>循环依赖异常<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name &lsquo;b&rsquo; defined in file [B.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name &lsquo;a&rsquo;: Requested bean is currently in creation: Is there an unresolvable circular reference?</div>
        </div>
    </div>
<p>英语不好的同学直接看最后一句话，<code>Is there an unresolvable circular reference?</code>，Spring 提示您：是否存在未解决的循环依赖？Of course！</p>
<p>那么，接下来就要进入源码进行分析了。</p>
<p>还是再来一段 Spring 官方文档的描述：</p>
<div class="details admonition quote open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-quote-right fa-fw"></i>正常 setter 依赖注入的过程<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">If no circular dependencies exist, when one or more collaborating beans are being injected into a dependent bean, each collaborating bean is totally configured prior to being injected into the dependent bean. This means that, if bean A has a dependency on bean B, the Spring IoC container completely configures bean B prior to invoking the setter method on bean A. In other words, the bean is instantiated (if it is not a pre-instantiated singleton), its dependencies are set, and the relevant lifecycle methods (such as a configured init method or the InitializingBean callback method) are invoked.</div>
        </div>
    </div>
<p>可以看到，在 setter 依赖且无循环依赖的情况下，如果 A 依赖于 B，那么 Spring 容器默认会率先完成 B 的所有创建和初始化过程，随后再进行 A 中 setter 方法的调用。同理，使用构造器进行依赖注入时，也会先触发 B 的创建和初始化，再将已经完成所有过程的 B 对象注入到 A 中，完成 A 的创建工作。</p>
<p>因此，这里将循环依赖分为三个过程：</p>
<ol>
<li>A 对象开始获取 <code>getBean(A)</code>，容器标记 A 对象正在创建中，准备构造器，发现构造器中有对于 B 的依赖，于是转向 B 的获取。</li>
<li>B 对象开始获取 <code>getBean(B)</code>，容器标记 B 对象正在创建中，准备构造器，发现构造器中有对于 A 的依赖，于是又准备开始 A 的获取。</li>
<li>再次进行 A 对象获取 <code>getBean(A)</code>，但发现 A 被标记为正在创建中，冲突，抛出异常。</li>
</ol>
<p>下图为上述过程的整个调用栈，从下到上分别对应上面的步骤：</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/27/wc76bNGPEzYieH9.png" title="https://s2.loli.net/2022/01/27/wc76bNGPEzYieH9.png" data-thumbnail="https://s2.loli.net/2022/01/27/wc76bNGPEzYieH9.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/27/wc76bNGPEzYieH9.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/27/wc76bNGPEzYieH9.png"
            alt="https://s2.loli.net/2022/01/27/wc76bNGPEzYieH9.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>将上图进行简化，提取一下其中的关键步骤，绘制流程图如下：</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/27/BUGgvVM9WERaqTb.png" title="https://s2.loli.net/2022/01/27/BUGgvVM9WERaqTb.png" data-thumbnail="https://s2.loli.net/2022/01/27/BUGgvVM9WERaqTb.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/27/BUGgvVM9WERaqTb.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/27/BUGgvVM9WERaqTb.png"
            alt="https://s2.loli.net/2022/01/27/BUGgvVM9WERaqTb.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>最后来看一下标记某对象正在创建中的代码，<code>getSingleton(beanName, singletonFactory)</code> 方法中调用了 <code>beforeSingletonCreation(beanName)</code> 方法，该方法也比较简单，直接使用 Set 进行重复 Bean 创建的判断，如下:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="cm">/** Names of beans that are currently in creation. */</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">final</span> <span class="n">Set</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">singletonsCurrentlyInCreation</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">        <span class="n">Collections</span><span class="o">.</span><span class="na">newSetFromMap</span><span class="o">(</span><span class="k">new</span> <span class="n">ConcurrentHashMap</span><span class="o">&lt;&gt;(</span><span class="n">16</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/** Names of beans currently excluded from in creation checks. */</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">final</span> <span class="n">Set</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">inCreationCheckExclusions</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">        <span class="n">Collections</span><span class="o">.</span><span class="na">newSetFromMap</span><span class="o">(</span><span class="k">new</span> <span class="n">ConcurrentHashMap</span><span class="o">&lt;&gt;(</span><span class="n">16</span><span class="o">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm">* Callback before singleton creation.
</span></span></span><span class="line"><span class="cl"><span class="cm">* &lt;p&gt;The default implementation register the singleton as currently in creation.
</span></span></span><span class="line"><span class="cl"><span class="cm">* @param beanName the name of the singleton about to be created
</span></span></span><span class="line"><span class="cl"><span class="cm">* @see #isSingletonCurrentlyInCreation
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span><span class="line"><span class="cl"><span class="kd">protected</span> <span class="kt">void</span> <span class="nf">beforeSingletonCreation</span><span class="o">(</span><span class="n">String</span> <span class="n">beanName</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">(!</span><span class="k">this</span><span class="o">.</span><span class="na">inCreationCheckExclusions</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="n">beanName</span><span class="o">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="k">this</span><span class="o">.</span><span class="na">singletonsCurrentlyInCreation</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">beanName</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">throw</span> <span class="k">new</span> <span class="n">BeanCurrentlyInCreationException</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h3 id="setter-注入方式">setter 注入方式<a hidden class="anchor" aria-hidden="true" href="#setter-注入方式">#</a></h3>
<p>主程序 <code>CircularReferenceDemo</code> 保持不变，将 A、B 修改如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Component</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">A</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">B</span> <span class="n">b</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@Autowired</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setB</span><span class="o">(</span><span class="n">B</span> <span class="n">b</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="o">.</span><span class="na">b</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@Component</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">B</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">A</span> <span class="n">a</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@Autowired</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setA</span><span class="o">(</span><span class="n">A</span> <span class="n">a</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="o">.</span><span class="na">a</span> <span class="o">=</span> <span class="n">a</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>发现可以正常通过运行而没有异常，说明循环依赖得到了解决，下面结合源码进行分析。</p>
<p>复习一下上面提到过的一个基本思路：在 setter 依赖且无循环依赖的情况下，如果 A 依赖于 B，那么 Spring 容器默认会率先完成 B 的所有创建和初始化过程，随后再进行 A 中 setter 方法的调用。</p>
<p>因此，先仿照构造器注入的方式给出一个猜测性的粗略流程：</p>
<ol>
<li>A 对象开始获取 <code>getBean(A)</code>，容器标记 A 对象正在创建中，使用默认构造器创建 A 对象（半成品 A&rsquo;），创建完成后开始填充属性（setter 方法），发现有 B 的依赖，转向 B 的获取。</li>
<li>B 对象开始获取 <code>getBean(B)</code>，容器标记 B 对象正在创建中，使用默认构造器创建 B 对象（半成品 B&rsquo;），创建完成后开始填充属性（setter 方法），发现有 A 的依赖，转向 A 的获取。</li>
<li>再次进行 A 对象获取 <code>getBean(A)</code>，<strong>在某个缓存中发现了 A 的半成品实例 A&rsquo;（注意这里的 A&rsquo; 与最终要返回的 A 在不考虑代理的情况下是同一个对象，只是它当前处于一种中间的临时状态，还没有完成 B 属性填充，且其自身初始化方法还未调用），直接使用这个半成品 A&rsquo; 完成 B 的属性填充，最终完成 A 的属性填充</strong>。</li>
</ol>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/28/bRctG2NovwJCKPj.png" title="https://s2.loli.net/2022/01/28/bRctG2NovwJCKPj.png" data-thumbnail="https://s2.loli.net/2022/01/28/bRctG2NovwJCKPj.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/28/bRctG2NovwJCKPj.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/28/bRctG2NovwJCKPj.png"
            alt="https://s2.loli.net/2022/01/28/bRctG2NovwJCKPj.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>关键的地方就是在 A 还未完全初始化完成时，提前「暴露」一个临时的半成品 A&rsquo; 给 B 进行依赖注入。暂时不太理解也没关系，后面会详细介绍这个缓存机制。</p>
<p>惯例先来一段相关调用栈：</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/28/VuBS8IJsvqL16dA.png" title="https://s2.loli.net/2022/01/28/VuBS8IJsvqL16dA.png" data-thumbnail="https://s2.loli.net/2022/01/28/VuBS8IJsvqL16dA.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/28/VuBS8IJsvqL16dA.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/28/VuBS8IJsvqL16dA.png"
            alt="https://s2.loli.net/2022/01/28/VuBS8IJsvqL16dA.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>同样可以发现分成了三个部分，其中很多方法与构造器方式一致，但它却解决了循环依赖的问题，因此需要更加细致地分析。</p>
<p>现在来详细地介绍一下 Spring 容器启动时，预创建非懒加载 Bean 的具体流程及 Spring 容器的几个重要缓存，涉及到的类较多（画过一个时序图，Mermaid 格式，比较完整，放到 <a href="https://gist.github.com/YazidLee/f5af97d997d7319103ee1051041acc6a">gist</a> 上了）。</p>
<p>下图是当前示例的 A、B 对象创建过程（看不清建议右键访问原图）：</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/28/bKtIhXqUzr2LcPn.png" title="https://s2.loli.net/2022/01/28/bKtIhXqUzr2LcPn.png" data-thumbnail="https://s2.loli.net/2022/01/28/bKtIhXqUzr2LcPn.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/28/bKtIhXqUzr2LcPn.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/28/bKtIhXqUzr2LcPn.png"
            alt="https://s2.loli.net/2022/01/28/bKtIhXqUzr2LcPn.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>其中，A 和 B 对象的创建流程基本一致，思路都是先尝试从缓存中获取，若缓存中不存在，则进行创建。创建的核心步骤在 <code>doCreateBean</code> 方法中，主要包括四个过程：</p>
<ul>
<li>创建对象实例 <code>createBeanInstance</code>：工厂方法、实例工厂方法、<code>Supplier</code> 或构造器等，本例中为默认构造器。这个方法调用之后，其实内存中已经创建好了一个 Bean 的实例对象，只不过是半成品，还没有调用 <code>populateBean</code> 完成属性填充，也未调用 <code>initializeBean</code> 完成初始化工作，这个状态的对象就是前文提到的「实例化」完成，但未进行初始化。</li>
<li>提前暴露半成品 <code>addSingletonFactory</code>：将上个步骤的半成品通过特殊包装放入缓存中，以解决循环依赖问题。</li>
<li>填充属性 <code>populateBean</code>：主要完成完成属性的填充、 <code>@Autowired</code> 的注入工作，从上一个调用栈图能看出调用的是 <code>AutowiredAnnotationBeanPostProcessor</code> 中的 <code>postProcessProperties</code> 方法完成注入。提前暴露方法 <code>addSingletonFactory</code> 在本方法之前调用也正是因为在 <code>populate</code> 过程中会触发依赖对象的获取过程，要在该过程前把当前的半成品放入缓存中。完成这个步骤的对象暂且称之为「属性填充完成」，但依然还未进行初始化。</li>
<li>自定义初始化 <code>initializeBean</code>：包括了 <code>Aware</code> 接口处理、<code>BeanPostPostProcessor</code> 的 before 和 after 逻辑、<code>@PostConstruct</code> 注解处理、<code>init-method</code> 处理、<code>InitializingBean</code> 处理等，注意在创建 A 的过程中，这个方法由于 <code>populate</code> 触发了依赖处理，因此将在 B 创建完成后才会被调用。这个方法执行完成后，我们才能把最终返回的对象称为「完全初始化」的对象，也就是所谓的成品。</li>
</ul>
<p>Spring 容器中的缓存位于 <code>DefaultSingletonBeanRegistry</code> 类中，具体定义如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="cm">/** Cache of singleton objects: bean name to bean instance. */</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">final</span> <span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">Object</span><span class="o">&gt;</span> <span class="n">singletonObjects</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ConcurrentHashMap</span><span class="o">&lt;&gt;(</span><span class="n">256</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/** Cache of singleton factories: bean name to ObjectFactory. */</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">final</span> <span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">ObjectFactory</span><span class="o">&lt;?&gt;&gt;</span> <span class="n">singletonFactories</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HashMap</span><span class="o">&lt;&gt;(</span><span class="n">16</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/** Cache of early singleton objects: bean name to bean instance. */</span>
</span></span><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">final</span> <span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">Object</span><span class="o">&gt;</span> <span class="n">earlySingletonObjects</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ConcurrentHashMap</span><span class="o">&lt;&gt;(</span><span class="n">16</span><span class="o">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>从名称及注释中能够获知它们分别的作用：</p>
<ul>
<li><code>singletonObjects</code>：保存已经完成所有创建过程（包括所有初始化过程）的单例 Bean，即所有的成品 Bean 都会被放到这个缓存中。</li>
<li><code>singletonFactories</code>：保存创建所有单例 Bean 的 <code>ObjectFactory</code> 对象，与代理有关，后续会详细介绍。</li>
<li><code>earlySingletonObjects</code>：保存实例化完成，但未进行 <code>populate</code> 和 <code>initializeBean</code> 的半成品 Bean。</li>
</ul>
<p>再回到创建流程图，橙色编号部分为缓存相关的方法，下面按编号顺序进行解析。</p>
<ol>
<li>
<p>A 对象获取过程的第一个 <code>getSingleton</code> 方法源码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">protected</span> <span class="n">Object</span> <span class="nf">getSingleton</span><span class="o">(</span><span class="n">String</span> <span class="n">beanName</span><span class="o">,</span> <span class="kt">boolean</span> <span class="n">allowEarlyReference</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">     <span class="c1">// Quick check for existing instance without full singleton lock
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>     <span class="n">Object</span> <span class="n">singletonObject</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">singletonObjects</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">     <span class="k">if</span> <span class="o">(</span><span class="n">singletonObject</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">isSingletonCurrentlyInCreation</span><span class="o">(</span><span class="n">beanName</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">         <span class="n">singletonObject</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">earlySingletonObjects</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">         <span class="k">if</span> <span class="o">(</span><span class="n">singletonObject</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">allowEarlyReference</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">             <span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">singletonObjects</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                 <span class="c1">// Consistent creation of early reference within full singleton lock
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                 <span class="n">singletonObject</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">singletonObjects</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                 <span class="k">if</span> <span class="o">(</span><span class="n">singletonObject</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                     <span class="n">singletonObject</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">earlySingletonObjects</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                     <span class="k">if</span> <span class="o">(</span><span class="n">singletonObject</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                         <span class="n">ObjectFactory</span><span class="o">&lt;?&gt;</span> <span class="n">singletonFactory</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">singletonFactories</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                         <span class="k">if</span> <span class="o">(</span><span class="n">singletonFactory</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                             <span class="n">singletonObject</span> <span class="o">=</span> <span class="n">singletonFactory</span><span class="o">.</span><span class="na">getObject</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                             <span class="k">this</span><span class="o">.</span><span class="na">earlySingletonObjects</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span> <span class="n">singletonObject</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                             <span class="k">this</span><span class="o">.</span><span class="na">singletonFactories</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                         <span class="o">}</span>
</span></span><span class="line"><span class="cl">                     <span class="o">}</span>
</span></span><span class="line"><span class="cl">                 <span class="o">}</span>
</span></span><span class="line"><span class="cl">             <span class="o">}</span>
</span></span><span class="line"><span class="cl">         <span class="o">}</span>
</span></span><span class="line"><span class="cl">     <span class="o">}</span>
</span></span><span class="line"><span class="cl">     <span class="k">return</span> <span class="n">singletonObject</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>首先从 <code>singletonObjects</code> 缓存中获取，没有获取到，且此时的 <code>isSingletonCurrentlyInCreation</code> 方法返回为 <code>false</code>（还没有对 A 进行标记），会直接返回 <code>null</code>。</p>
<p>假定 <code>isSingletonCurrentlyInCreation</code> 为 <code>true</code>，再从 <code>earlySingletonObjects</code> 中尝试获取，若仍为空且 <code>allowEarlyReference</code> 为 <code>true</code>（默认值），则加锁以保证后续操作的强一致性。对单例设计模式有了解的朋友这里肯定很熟悉，类似于 <a href="https://en.wikipedia.org/wiki/Double-checked_locking">DCL</a> 的操作。最后，尝试从 <code>singletonFactories</code> 中获取对象，因为我们这里是第一次进入，因此所有缓存都为空，最终返回 <code>null</code>。</p>
</li>
<li>
<p>缓存中不存在任何 A 对象，因此进入编号为 2 的方法 <code>getSingleton()</code>（注意区别上面那个 <code>getSingleton</code> 方法），这个方法有两个参数，第一个参数为 <code>beanName</code>，第二个参数为 <code>ObjectFactory</code>，Spring 源码中给出的调用参数如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">sharedInstance</span> <span class="o">=</span> <span class="n">getSingleton</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">     <span class="k">try</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">         <span class="k">return</span> <span class="n">createBean</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span> <span class="n">mbd</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">     <span class="o">}</span>
</span></span><span class="line"><span class="cl">     <span class="k">catch</span> <span class="o">(</span><span class="n">BeansException</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">         <span class="c1">// Explicitly remove instance from singleton cache: It might have been put there
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>         <span class="c1">// eagerly by the creation process, to allow for circular reference resolution.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>         <span class="c1">// Also remove any beans that received a temporary reference to the bean.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>         <span class="n">destroySingleton</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">         <span class="k">throw</span> <span class="n">ex</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">     <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">});</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>第二个参数使用了 lambda 表达式，类型推断为 <code>ObjectFactory</code>，因此，在 <code>getSingleton</code> 这个方法中一定会调用 <code>ObjectFactory</code> 的 <code>getObject</code> 方法，如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="hl"><span class="lnt">14
</span></span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="hl"><span class="lnt">21
</span></span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="hl"><span class="lnt">44
</span></span><span class="lnt">45
</span><span class="lnt">46
</span><span class="hl"><span class="lnt">47
</span></span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">Object</span> <span class="nf">getSingleton</span><span class="o">(</span><span class="n">String</span> <span class="n">beanName</span><span class="o">,</span> <span class="n">ObjectFactory</span><span class="o">&lt;?&gt;</span> <span class="n">singletonFactory</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">     <span class="n">Assert</span><span class="o">.</span><span class="na">notNull</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span> <span class="s">&#34;Bean name must not be null&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">     <span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">singletonObjects</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">         <span class="n">Object</span> <span class="n">singletonObject</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">singletonObjects</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">         <span class="k">if</span> <span class="o">(</span><span class="n">singletonObject</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">             <span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">singletonsCurrentlyInDestruction</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                 <span class="k">throw</span> <span class="k">new</span> <span class="n">BeanCreationNotAllowedException</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">                         <span class="s">&#34;Singleton bean creation not allowed while singletons of this factory are in destruction &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                         <span class="s">&#34;(Do not request a bean from a BeanFactory in a destroy method implementation!)&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">             <span class="o">}</span>
</span></span><span class="line"><span class="cl">             <span class="k">if</span> <span class="o">(</span><span class="n">logger</span><span class="o">.</span><span class="na">isDebugEnabled</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                 <span class="n">logger</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">&#34;Creating shared instance of singleton bean &#39;&#34;</span> <span class="o">+</span> <span class="n">beanName</span> <span class="o">+</span> <span class="s">&#34;&#39;&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">             <span class="o">}</span>
</span></span><span class="line hl"><span class="cl">             <span class="n">beforeSingletonCreation</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">             <span class="kt">boolean</span> <span class="n">newSingleton</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">             <span class="kt">boolean</span> <span class="n">recordSuppressedExceptions</span> <span class="o">=</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">suppressedExceptions</span> <span class="o">==</span> <span class="kc">null</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">             <span class="k">if</span> <span class="o">(</span><span class="n">recordSuppressedExceptions</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                 <span class="k">this</span><span class="o">.</span><span class="na">suppressedExceptions</span> <span class="o">=</span> <span class="k">new</span> <span class="n">LinkedHashSet</span><span class="o">&lt;&gt;();</span>
</span></span><span class="line"><span class="cl">             <span class="o">}</span>
</span></span><span class="line"><span class="cl">             <span class="k">try</span> <span class="o">{</span>
</span></span><span class="line hl"><span class="cl">                 <span class="n">singletonObject</span> <span class="o">=</span> <span class="n">singletonFactory</span><span class="o">.</span><span class="na">getObject</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">                 <span class="n">newSingleton</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">             <span class="o">}</span>
</span></span><span class="line"><span class="cl">             <span class="k">catch</span> <span class="o">(</span><span class="n">IllegalStateException</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                 <span class="c1">// Has the singleton object implicitly appeared in the meantime -&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                 <span class="c1">// if yes, proceed with it since the exception indicates that state.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                 <span class="n">singletonObject</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">singletonObjects</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                 <span class="k">if</span> <span class="o">(</span><span class="n">singletonObject</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                     <span class="k">throw</span> <span class="n">ex</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">                 <span class="o">}</span>
</span></span><span class="line"><span class="cl">             <span class="o">}</span>
</span></span><span class="line"><span class="cl">             <span class="k">catch</span> <span class="o">(</span><span class="n">BeanCreationException</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                 <span class="k">if</span> <span class="o">(</span><span class="n">recordSuppressedExceptions</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                     <span class="k">for</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">suppressedException</span> <span class="o">:</span> <span class="k">this</span><span class="o">.</span><span class="na">suppressedExceptions</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                         <span class="n">ex</span><span class="o">.</span><span class="na">addRelatedCause</span><span class="o">(</span><span class="n">suppressedException</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">                     <span class="o">}</span>
</span></span><span class="line"><span class="cl">                 <span class="o">}</span>
</span></span><span class="line"><span class="cl">                 <span class="k">throw</span> <span class="n">ex</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">             <span class="o">}</span>
</span></span><span class="line"><span class="cl">             <span class="k">finally</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                 <span class="k">if</span> <span class="o">(</span><span class="n">recordSuppressedExceptions</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                     <span class="k">this</span><span class="o">.</span><span class="na">suppressedExceptions</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">                 <span class="o">}</span>
</span></span><span class="line hl"><span class="cl">                 <span class="n">afterSingletonCreation</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">             <span class="o">}</span>
</span></span><span class="line"><span class="cl">             <span class="k">if</span> <span class="o">(</span><span class="n">newSingleton</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line hl"><span class="cl">                 <span class="n">addSingleton</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span> <span class="n">singletonObject</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">             <span class="o">}</span>
</span></span><span class="line"><span class="cl">         <span class="o">}</span>
</span></span><span class="line"><span class="cl">         <span class="k">return</span> <span class="n">singletonObject</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">     <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>重点关注高亮几行：</p>
<ul>
<li><code>beforeSingletonCreation</code> 在构造器方式中以及介绍过了，通过 Set 对正在创建的 Bean 进行标记。</li>
<li><code>singletonObject = singletonFactory.getObject();</code> 果然调用了 <code>ObjectFactory</code> 的 <code>getObject</code> 方法，因此会进入 <code>createBean</code> 方法，稍后介绍。</li>
<li><code>afterSingletonCreation</code> 方法作用与 <code>beforeSingletonCreation</code> 类似，在上一步 <code>createBean</code> 创建完成后，将正在创建的标记清除。</li>
<li><code>addSingleton</code> 这个方法是一个重要方法，它将创建完成的 Bean 放入 <code>singletonObjects</code> 缓存中，并从 <code>earlySingletonObjects</code> 和 <code>singletonFactories</code> 中移除相应的同名临时对象。稍后在 8、9 中会详细介绍。</li>
</ul>
</li>
<li>
<p>到目前为止，缓存中的仍然没有任何对象。进入到 <code>addSingletonFactory</code> 之前，在 <code>doCreateBean</code> 中会先调用在构造器方式中介绍过的 <code>createBeanInstance</code> 方法，该方法会选择适当的方式（本例中是默认构造器）将目标对象进行实例化（具体定位到 <code>SimpleInstantiationStrategy</code> 中的 <code>instantiate</code> 方法，再调用<code>BeanUtils.instantiateClass(constructorToUse)</code>，继续跟进，会找到 <code>ctor.newInstance(argsWithDefaultValues)</code>，即反射方式创建实例）。</p>
<p>此时，本例中的 A 对象已经创建，但没有进行属性填充、初始化，是半成品对象，接下来就将这个半成品对象通过下面代码放入缓存中：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="hl"><span class="lnt">10
</span></span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="c1">// Eagerly cache singletons to be able to resolve circular references
</span></span></span><span class="line"><span class="cl"><span class="c1">// even when triggered by lifecycle interfaces like BeanFactoryAware.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">boolean</span> <span class="n">earlySingletonExposure</span> <span class="o">=</span> <span class="o">(</span><span class="n">mbd</span><span class="o">.</span><span class="na">isSingleton</span><span class="o">()</span> <span class="o">&amp;&amp;</span> <span class="k">this</span><span class="o">.</span><span class="na">allowCircularReferences</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="n">isSingletonCurrentlyInCreation</span><span class="o">(</span><span class="n">beanName</span><span class="o">));</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">(</span><span class="n">earlySingletonExposure</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">(</span><span class="n">logger</span><span class="o">.</span><span class="na">isTraceEnabled</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">logger</span><span class="o">.</span><span class="na">trace</span><span class="o">(</span><span class="s">&#34;Eagerly caching bean &#39;&#34;</span> <span class="o">+</span> <span class="n">beanName</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                <span class="s">&#34;&#39; to allow for resolving potential circular references&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line hl"><span class="cl">    <span class="n">addSingletonFactory</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="n">getEarlyBeanReference</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span> <span class="n">mbd</span><span class="o">,</span> <span class="n">bean</span><span class="o">));</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>addSingletonFactory</code> 方法第二个参数同样为 <code>ObjectFactory</code>，具体内容也比较简单：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">protected</span> <span class="kt">void</span> <span class="nf">addSingletonFactory</span><span class="o">(</span><span class="n">String</span> <span class="n">beanName</span><span class="o">,</span> <span class="n">ObjectFactory</span><span class="o">&lt;?&gt;</span> <span class="n">singletonFactory</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Assert</span><span class="o">.</span><span class="na">notNull</span><span class="o">(</span><span class="n">singletonFactory</span><span class="o">,</span> <span class="s">&#34;Singleton factory must not be null&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">singletonObjects</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(!</span><span class="k">this</span><span class="o">.</span><span class="na">singletonObjects</span><span class="o">.</span><span class="na">containsKey</span><span class="o">(</span><span class="n">beanName</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="o">.</span><span class="na">singletonFactories</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span> <span class="n">singletonFactory</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="o">.</span><span class="na">earlySingletonObjects</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">this</span><span class="o">.</span><span class="na">registeredSingletons</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>几个简单的操作相信都能看懂，该方法执行完成后，<code>singletonFactories</code> 缓存中出现了数据，三个 Map 的状态如下：</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/28/3xzXa7W48QYBlcM.png" title="https://s2.loli.net/2022/01/28/3xzXa7W48QYBlcM.png" data-thumbnail="https://s2.loli.net/2022/01/28/3xzXa7W48QYBlcM.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/28/3xzXa7W48QYBlcM.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/28/3xzXa7W48QYBlcM.png"
            alt="https://s2.loli.net/2022/01/28/3xzXa7W48QYBlcM.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>A 对象暂时还没创建完成，接下来进入 <code>populateBean</code> 方法进行属性填充，该方法会触发 B 对象的获取，因此，会再次进入 <code>getSingleton</code> 方法以获取 B。</p>
</li>
<li>
<p>此次进入 <code>getSingleton</code> 目的是获取 B 对象，由于 B 对象在 <code>singletonObjects</code> 中不存在，且 <code>isSingletonCurrentlyInCreation</code> 返回为 <code>false</code>，因此直接返回 <code>null</code>。</p>
</li>
<li>
<p>参考 2 过程。</p>
</li>
<li>
<p>参考 3 过程，完成后，此时三个 Map 的状态更新为：</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/28/wN5fJIR4Y2FkLot.png" title="https://s2.loli.net/2022/01/28/wN5fJIR4Y2FkLot.png" data-thumbnail="https://s2.loli.net/2022/01/28/wN5fJIR4Y2FkLot.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/28/wN5fJIR4Y2FkLot.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/28/wN5fJIR4Y2FkLot.png"
            alt="https://s2.loli.net/2022/01/28/wN5fJIR4Y2FkLot.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>同样,这里由于 B 的 <code>@Autowired</code> 依赖了 A，因此，会进入 <code>getSingleton</code> 再次获取 A。</p>
</li>
<li>
<p>注意，本次的 <code>getSingleton</code> 方法调用将与第一次有所不同，此时，虽然 <code>singletonObjects</code> 仍然没有对象，但 <code>isSingletonCurrentlyInCreation</code> 的返回值为 <code>true</code>。因此会进入到加锁块中：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="hl"><span class="lnt">3
</span></span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"> <span class="n">ObjectFactory</span><span class="o">&lt;?&gt;</span> <span class="n">singletonFactory</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">singletonFactories</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="o">(</span><span class="n">singletonFactory</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line hl"><span class="cl">     <span class="n">singletonObject</span> <span class="o">=</span> <span class="n">singletonFactory</span><span class="o">.</span><span class="na">getObject</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">     <span class="k">this</span><span class="o">.</span><span class="na">earlySingletonObjects</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span> <span class="n">singletonObject</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">     <span class="k">this</span><span class="o">.</span><span class="na">singletonFactories</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>表面意思也比较简单，调用 <code>singletonFactories</code> 中 a 对应的 <code>ObjectFactory</code> 的 <code>getObject</code> 方法，获取半成品对象，并将该对象放入到 <code>earlySingletonObjects</code> 中，最后将该 <code>ObjectFactory</code> 从 <code>singletonFactories</code> 中移除。此时，三个 Map 的状态为：</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/28/KBfoG5lrnwOPZsg.png" title="https://s2.loli.net/2022/01/28/KBfoG5lrnwOPZsg.png" data-thumbnail="https://s2.loli.net/2022/01/28/KBfoG5lrnwOPZsg.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/28/KBfoG5lrnwOPZsg.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/28/KBfoG5lrnwOPZsg.png"
            alt="https://s2.loli.net/2022/01/28/KBfoG5lrnwOPZsg.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>还需要关注这里高亮行的代码，还记得这里的 <code>singletonFactory</code> 是什么东西吗？在 3 过程中，<code>addSingletonFactory(beanName, () -&gt; getEarlyBeanReference(beanName, mbd, bean));</code> 将 lambda 表达式存入了 <code>singletonFactories</code>，因此这里调用的 <code>getObject</code> 实际上就是调用 <code> getEarlyBeanReference(beanName, mbd, bean))</code>，它的代码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">protected</span> <span class="n">Object</span> <span class="nf">getEarlyBeanReference</span><span class="o">(</span><span class="n">String</span> <span class="n">beanName</span><span class="o">,</span> <span class="n">RootBeanDefinition</span> <span class="n">mbd</span><span class="o">,</span> <span class="n">Object</span> <span class="n">bean</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">     <span class="n">Object</span> <span class="n">exposedObject</span> <span class="o">=</span> <span class="n">bean</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">if</span> <span class="o">(!</span><span class="n">mbd</span><span class="o">.</span><span class="na">isSynthetic</span><span class="o">()</span> <span class="o">&amp;&amp;</span> <span class="n">hasInstantiationAwareBeanPostProcessors</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">         <span class="k">for</span> <span class="o">(</span><span class="n">SmartInstantiationAwareBeanPostProcessor</span> <span class="n">bp</span> <span class="o">:</span> <span class="n">getBeanPostProcessorCache</span><span class="o">().</span><span class="na">smartInstantiationAware</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">             <span class="n">exposedObject</span> <span class="o">=</span> <span class="n">bp</span><span class="o">.</span><span class="na">getEarlyBeanReference</span><span class="o">(</span><span class="n">exposedObject</span><span class="o">,</span> <span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">         <span class="o">}</span>
</span></span><span class="line"><span class="cl">     <span class="o">}</span>
</span></span><span class="line"><span class="cl">     <span class="k">return</span> <span class="n">exposedObject</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>这里的过程是要调用 <code>SmartInstantiationAwareBeanPostProcessor</code> 中的 <code>getEarlyBeanReference</code> 方法，将原始的半成品实例进行包装，典型的应用就是在这里给 AOP 提供一个包装的机会，试想如果我们示例中的 A 对象需要被代理，那么在这里就会给 A 对象的半成品 A&rsquo; 进行包装，让等待依赖注入的 B 最终能够得到的是 AOP 包装后的 A，而不是原始的 A 对象。但本例中的 A 不需要代理，因此直接返回 A&rsquo; 对象即可。</p>
<p>既然已经有了一个 A&rsquo; 对象，那么就满足了 B 的需求，随后返回到 B 的 <code>populateBean</code> 方法，直接将 A&rsquo; 注入到 B 对象中，完成 B 的属性填充，并调用 <code>initializeBean</code> 对 B 进行初始化。</p>
</li>
<li>
<p>这时 B 对象已经走完了整个创建的过程，是一个成品对象，要将它放入到成品缓存 <code>singletonObjects</code> 中。代码将返回到 5 过程，具体代码位置参考 2 过程中的 <code>getSingleton</code> 第 21 行高亮处。接下来将要调用 <code>afterSingletonCreation</code> 完成清理工作，将 B 对象的正在创建状态移除。但更重要的是 <code>addSingleton(beanName, singletonObject)</code> 方法：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">protected</span> <span class="kt">void</span> <span class="nf">addSingleton</span><span class="o">(</span><span class="n">String</span> <span class="n">beanName</span><span class="o">,</span> <span class="n">Object</span> <span class="n">singletonObject</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">     <span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">singletonObjects</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">         <span class="k">this</span><span class="o">.</span><span class="na">singletonObjects</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">beanName</span><span class="o">,</span> <span class="n">singletonObject</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">         <span class="k">this</span><span class="o">.</span><span class="na">singletonFactories</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">         <span class="k">this</span><span class="o">.</span><span class="na">earlySingletonObjects</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">         <span class="k">this</span><span class="o">.</span><span class="na">registeredSingletons</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">     <span class="o">}</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>它将创建完成的 B 对象放入 <code>singletonObjects</code> 中，并从 <code>singletonFactories</code> 和 <code>earlySingletonObjects</code> 中移除同名的临时对象。至此，B 对象创建完成。</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/28/uiMEAZF2CcV7ze1.png" title="https://s2.loli.net/2022/01/28/uiMEAZF2CcV7ze1.png" data-thumbnail="https://s2.loli.net/2022/01/28/uiMEAZF2CcV7ze1.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/28/uiMEAZF2CcV7ze1.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/28/uiMEAZF2CcV7ze1.png"
            alt="https://s2.loli.net/2022/01/28/uiMEAZF2CcV7ze1.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>同样道理，既然 B 已经完成了创建，就会返回到 A 的 <code>populateBean</code> 方法，将 B 属性注入 A，并调用 <code>initializeBean</code> 对 A 进行初始化。</p>
</li>
<li>
<p>同过程 8，此时要将完成创建工作的 A 对象放入 <code>singletonObjects</code>。至此，A 对象也创建完成，循环依赖得到了解决。</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/01/28/YMyO8r416T9XAsC.png" title="https://s2.loli.net/2022/01/28/YMyO8r416T9XAsC.png" data-thumbnail="https://s2.loli.net/2022/01/28/YMyO8r416T9XAsC.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/01/28/YMyO8r416T9XAsC.png"
            loading="lazy"
            title="https://s2.loli.net/2022/01/28/YMyO8r416T9XAsC.png"
            alt="https://s2.loli.net/2022/01/28/YMyO8r416T9XAsC.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
</li>
</ol>
<hr>
<h2 id="aop-和循环依赖">AOP 和循环依赖<a hidden class="anchor" aria-hidden="true" href="#aop-和循环依赖">#</a></h2>
<p>AOP 是 Spring 的核心功能之一，而 Spring 中的 AOP 实现手段主要是「动态代理」，无论是 JDK 亦或是 CGLIB，都是针对<strong>方法</strong>层面进行增强。本节将首先介绍 AOP 的核心概念及部分 API，然后再对 AOP 代理对象间的循环依赖进行分析。</p>
<h3 id="aop-介绍">AOP 介绍<a hidden class="anchor" aria-hidden="true" href="#aop-介绍">#</a></h3>
<p>简单来说，Spring 中的 AOP 就是向外提供一个「代理对象」，该对象中封装了所有适配的拦截器和原始对象的目标方法，成为一个调用链，在调用代理对象的方法时处理调用链中的所有逻辑（包括目标方法本身），以达到动态增强的效果，顺带一提，Spring 中「代理对象」主要有两种实现：<code>JdkDynamicAopProxy</code> 和 <code>CglibAopProxy</code>，它们的 <code>getProxy</code> 方法可以返回我们所需的「代理对象」，感兴趣的可以大致浏览下源码，核心部分都是拦截器链的封装和调用。</p>
<p>这里普及一下 AOP 中一些 API，涉及了部分源码。实际工作中，我们通常都是使用 <code>@Aspect</code> 注解的方式来声明切面类（包含 <code>@Pointcut</code> 切点描述及 <code>@Before</code>、<code>@After</code> 等通知），但实际上 Spring 提供了一套清晰可用的编程接口，可以灵活地进行定制实现。下面先介绍一些基本的接口：</p>
<ul>
<li>
<p><code>JoinPoint</code>：AOP 中的<strong>连接点</strong>，在 Spring 只支持方法层面的增强，这点从它的唯一子接口 <code>MethodInvacation</code> 中也可以看出，因此直接理解为方法即可。</p>
</li>
<li>
<p><code>Pointcut</code>：AOP 中的<strong>切点</strong>，作用是从所有的<strong>连接点</strong>中进行筛选操作，它不具备任何的具体业务逻辑，仅仅是选出我们所需要增强的方法。</p>
</li>
<li>
<p><code>Advice</code>：AOP 中的<strong>通知</strong>，它是具体增强业务的实现，Spring 中支持 <code>before</code>、<code>after</code>、<code>afterReturning</code>、<code>afterThrows</code> 和 <code>around</code> 这几个类型的通知，它协同 <code>Pointcut</code> 在特定的方法上在特定「位置」实现增强操作。</p>
</li>
<li>
<p><code>Advisor</code>：Spring 中的一个中间接口，主要使用其子接口 <code>PointcutAdivsor</code>，它将 <code>Pointcut</code> 和 <code>Advice</code> 进行组合，放在一个 <code>Advisor</code> 类中，方便使用。</p>
</li>
<li>
<p><code>MethodInterceptor</code>：字面意思是<strong>方法拦截器</strong>，它是 <code>Advice</code> 的子接口，Spirng 中的 <code>Around</code> 通知就是使用它来实现的，且所有的其他通知都会交由相应适配器处理，最终都转换为 <code>MethodInterceptor</code> 放入「代理对象」的拦截器链中。</p>
</li>
<li>
<p><code>ProxyFactory</code>：创建「代理对象」的工厂类，它是 <code>AdvisedSupport</code> 的子类，提供了一系列配置的方法，内部使用 <code>DefaultAopProxyFactory</code> 创建具体的「代理对象」，<code>DefaultAopProxyFactory</code> 核心代码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="hl"><span class="lnt">12
</span></span><span class="lnt">13
</span><span class="lnt">14
</span><span class="hl"><span class="lnt">15
</span></span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Override</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">AopProxy</span> <span class="nf">createAopProxy</span><span class="o">(</span><span class="n">AdvisedSupport</span> <span class="n">config</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">AopConfigException</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">(!</span><span class="n">NativeDetector</span><span class="o">.</span><span class="na">inNativeImage</span><span class="o">()</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">            <span class="o">(</span><span class="n">config</span><span class="o">.</span><span class="na">isOptimize</span><span class="o">()</span> <span class="o">||</span> <span class="n">config</span><span class="o">.</span><span class="na">isProxyTargetClass</span><span class="o">()</span> <span class="o">||</span> <span class="n">hasNoUserSuppliedProxyInterfaces</span><span class="o">(</span><span class="n">config</span><span class="o">)))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">targetClass</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">getTargetClass</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">targetClass</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">throw</span> <span class="k">new</span> <span class="n">AopConfigException</span><span class="o">(</span><span class="s">&#34;TargetSource cannot determine target class: &#34;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                    <span class="s">&#34;Either an interface or a target is required for proxy creation.&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">targetClass</span><span class="o">.</span><span class="na">isInterface</span><span class="o">()</span> <span class="o">||</span> <span class="n">Proxy</span><span class="o">.</span><span class="na">isProxyClass</span><span class="o">(</span><span class="n">targetClass</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// JDK 动态代理
</span></span></span><span class="line hl"><span class="cl"><span class="c1"></span>            <span class="k">return</span> <span class="k">new</span> <span class="n">JdkDynamicAopProxy</span><span class="o">(</span><span class="n">config</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// CGLIB 动态代理，ObjenesisCglibAopProxy 是 CglibAopProxy 的子类
</span></span></span><span class="line hl"><span class="cl"><span class="c1"></span>        <span class="k">return</span> <span class="k">new</span> <span class="n">ObjenesisCglibAopProxy</span><span class="o">(</span><span class="n">config</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="n">JdkDynamicAopProxy</span><span class="o">(</span><span class="n">config</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p><code>AbstractAutoProxyCreator</code>：与 Spring IOC 结合，实现 Spring 容器 AOP 代理对象的自动创建功能，本质上它是<code>SmartInstantiationAwareBeanPostProcessor</code>，实现了以下几个重要的逻辑：</p>
<ul>
<li><code>postProcessBeforeInstantiation</code>：在 Bean 实例化前被调用，调用了 <code>isInfrustructureClass</code> 和 <code>shouldSkip</code> 来决定当前 Bean 是否应该被代理，其中 <code>shouldSkip</code> 会触发 <code>findEligibleAdvisors</code> 的逻辑，将容器中所有切面逻辑进行筛选。同时包含了处理自定义 <code>TargetSource</code> 的业务（不常用，具体可以查看官方文档，与热替换和池化技术关联较大）。</li>
<li><code>getEarlyReference</code>：如果当前 Bean  需要被代理，则在解决循环依赖的过程中，需要提前暴露这个「代理对象」，以替换原对象，成为注入到其他 Bean 中的真正对象。核心方法 <code>wrapIfNecessary</code>。</li>
<li><code>postProcessAfterInitialization</code>：在初始化后调用，属于 <code>BeanPostProcessor</code> 接口中的方法，同样调用了 <code>wrapIfNecessary</code> 方法，在对象创建完成且属性填充完成后进行生成代理操作。</li>
</ul>
</li>
</ul>
<p>有了上述这些基本概念后，下面是两个具体的实例，分别展示了 Spring 手动创建代理和自动创建代理：</p>
<ul>
<li>
<p>手动创建</p>
<p>目标类：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ManualA</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="kt">void</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Executing A...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="kt">void</span> <span class="nf">other</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Other method...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>实现手动创建代理：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ManualProxyDemo</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">static</span> <span class="kd">class</span> <span class="nc">ManualMethodInterceptor</span> <span class="kd">implements</span> <span class="n">MethodInterceptor</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="nd">@Nullable</span>
</span></span><span class="line"><span class="cl">      <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">      <span class="kd">public</span> <span class="n">Object</span> <span class="nf">invoke</span><span class="o">(</span><span class="nd">@Nonnull</span> <span class="n">MethodInvocation</span> <span class="n">invocation</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;---------------------&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Enhancement Before...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">Object</span> <span class="n">result</span> <span class="o">=</span> <span class="n">invocation</span><span class="o">.</span><span class="na">proceed</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">          <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Enhancement After...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;---------------------&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="n">result</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">      <span class="o">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="n">Advice</span> <span class="nf">methodInterceptor</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">new</span> <span class="n">ManualMethodInterceptor</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="n">Pointcut</span> <span class="nf">manualPointcut</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">new</span> <span class="n">StaticMethodMatcherPointcut</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">          <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">          <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">matches</span><span class="o">(</span><span class="n">Method</span> <span class="n">method</span><span class="o">,</span> <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">targetClass</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">              <span class="c1">// 切点，只匹配方法名为 execute 的方法
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="k">return</span> <span class="s">&#34;execute&#34;</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">          <span class="o">}</span>
</span></span><span class="line"><span class="cl">      <span class="o">};</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="n">Advisor</span> <span class="nf">manualAdvisor</span><span class="o">(</span><span class="n">Pointcut</span> <span class="n">pointcut</span><span class="o">,</span> <span class="n">Advice</span> <span class="n">methodInterceptor</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">new</span> <span class="n">DefaultPointcutAdvisor</span><span class="o">(</span><span class="n">pointcut</span><span class="o">,</span> <span class="n">methodInterceptor</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">ManualProxyDemo</span> <span class="n">config</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ManualProxyDemo</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// Pointcut、Advice、Advisor 准备
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">Pointcut</span> <span class="n">pointcut</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">manualPointcut</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">      <span class="n">Advice</span> <span class="n">advice</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">methodInterceptor</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">      <span class="n">Advisor</span> <span class="n">advisor</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">manualAdvisor</span><span class="o">(</span><span class="n">pointcut</span><span class="o">,</span> <span class="n">advice</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// 目标对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">ManualA</span> <span class="n">target</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ManualA</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// 代理工厂，设置目标对象
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">ProxyFactory</span> <span class="n">proxyFactory</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ProxyFactory</span><span class="o">(</span><span class="n">target</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 配置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="c1">// 使用 CGLIB 动态代理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">proxyFactory</span><span class="o">.</span><span class="na">setProxyTargetClass</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 将我们准备好的 Advisor 设置到工厂中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">proxyFactory</span><span class="o">.</span><span class="na">addAdvisor</span><span class="o">(</span><span class="n">advisor</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// 创建「代理对象」
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">ManualA</span> <span class="n">proxy</span> <span class="o">=</span> <span class="o">(</span><span class="n">ManualA</span><span class="o">)</span> <span class="n">proxyFactory</span><span class="o">.</span><span class="na">getProxy</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 使用「代理对象」执行符合 Pointcut 的目标方法
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">proxy</span><span class="o">.</span><span class="na">execute</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 使用「代理对象」执行其他不符合的方法
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">proxy</span><span class="o">.</span><span class="na">other</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>手动创建代理的流程也比较简单，准备好 <code>Pointcut</code>、<code>Advice</code> 对象，并封装为 <code>Advisor</code> 添加到 <code>ProxyFactory</code>中（也可以直接添加 <code>Advice</code>，工厂会自动进行封装），<code>ProxyFactory</code> 可在构造函数中指定需要代理的目标对象，且可以设置一些通用属性（如 proxyTargetClass、exposeProxy 等）。最终调用 <code>getProxy</code> 方法获取我们所需要的「代理对象」。执行上面代码后，结果如下：符合 <code>Pointcut</code> 的方法 <code>execute</code> 得到了增强，而 <code>other</code> 方法则直接调用原始对象中的同名方法。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">---------------------
</span></span><span class="line"><span class="cl">Enhancement Before...
</span></span><span class="line"><span class="cl">Executing A...
</span></span><span class="line"><span class="cl">Enhancement After...
</span></span><span class="line"><span class="cl">---------------------
</span></span><span class="line"><span class="cl">Other method...
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p>自动创建</p>
<p>目标类中新增一个注解和内部方法调用：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="hl"><span class="lnt"> 1
</span></span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="hl"><span class="lnt"> 8
</span></span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line hl"><span class="cl"><span class="nd">@Component</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ManualA</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="kt">void</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Executing A...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="kt">void</span> <span class="nf">other</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line hl"><span class="cl">      <span class="n">execute</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">      <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Other method...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>自动创建代理，新增 <code>@EnableAspectJAutoProxy</code> 注解和 <code>@Bean</code>、<code>@Primary</code>，移除手动创建的代码，取而代之的是使用 Spring IOC 自动创建的方式：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="hl"><span class="lnt"> 2
</span></span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@ComponentScan</span>
</span></span><span class="line hl"><span class="cl"><span class="nd">@EnableAspectJAutoProxy</span><span class="o">(</span><span class="n">proxyTargetClass</span> <span class="o">=</span> <span class="kc">true</span><span class="o">,</span> <span class="n">exposeProxy</span> <span class="o">=</span> <span class="kc">true</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ManualProxyDemo</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="kd">class</span> <span class="nc">ManualMethodInterceptor</span> <span class="kd">implements</span> <span class="n">MethodInterceptor</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="nd">@Nullable</span>
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="n">Object</span> <span class="nf">invoke</span><span class="o">(</span><span class="nd">@Nonnull</span> <span class="n">MethodInvocation</span> <span class="n">invocation</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;---------------------&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Enhancement Before...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">Object</span> <span class="n">result</span> <span class="o">=</span> <span class="n">invocation</span><span class="o">.</span><span class="na">proceed</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Enhancement After...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;---------------------&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">result</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// @Bean 注解将 MethodInterceptor 交由 Spring 管理，@Primary 防止其他 MethodInterceptor 干扰
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nd">@Bean</span>
</span></span><span class="line"><span class="cl">  <span class="nd">@Primary</span>
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="n">Advice</span> <span class="nf">methodInterceptor</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">new</span> <span class="n">ManualMethodInterceptor</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nd">@Bean</span>
</span></span><span class="line"><span class="cl">  <span class="nd">@Primary</span>
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="n">Pointcut</span> <span class="nf">manualPointcut</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">new</span> <span class="n">StaticMethodMatcherPointcut</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">          <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">          <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">matches</span><span class="o">(</span><span class="n">Method</span> <span class="n">method</span><span class="o">,</span> <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">targetClass</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">              <span class="c1">// 切点，只匹配方法名为 execute 的方法
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="k">return</span> <span class="s">&#34;execute&#34;</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">          <span class="o">}</span>
</span></span><span class="line"><span class="cl">      <span class="o">};</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nd">@Bean</span>
</span></span><span class="line"><span class="cl">  <span class="nd">@Primary</span>
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="n">Advisor</span> <span class="nf">manualAdvisor</span><span class="o">(</span><span class="n">Pointcut</span> <span class="n">pointcut</span><span class="o">,</span> <span class="n">Advice</span> <span class="n">methodInterceptor</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">new</span> <span class="n">DefaultPointcutAdvisor</span><span class="o">(</span><span class="n">pointcut</span><span class="o">,</span> <span class="n">methodInterceptor</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 结合 Spring 容器使用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">AnnotationConfigApplicationContext</span> <span class="n">context</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AnnotationConfigApplicationContext</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">      <span class="n">context</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">ManualProxyDemo</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// 启动容器，过程中创建 Bean 实例，并完成「代理对象」的自动创建
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">context</span><span class="o">.</span><span class="na">refresh</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// 从容器中获取对象，该对象为我们需要的「代理对象」
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">ManualA</span> <span class="n">proxy</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="n">ManualA</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 打印 proxy 的类名
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">proxy</span><span class="o">.</span><span class="na">getClass</span><span class="o">().</span><span class="na">getName</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 执行结果与手动创建类似
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">proxy</span><span class="o">.</span><span class="na">execute</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 注意这个方法，它内部调用了 execute 方法
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">proxy</span><span class="o">.</span><span class="na">other</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>与手动创建代理对象相比，首先需要使用 <code>@EnableAspectJAutoProxy</code> 将自动创建代理的相关类导入。具体来说，在 <code>@EnableAspectJAutoProxy</code> 类上可以找到 <code>@Import(AspectJAutoProxyRegistrar.class)</code>。进入 <code>AspectJAutoProxyRegistrar</code> 的 <code>registerBeanDefinitions</code> 方法及相关 <code>AopConfigUtils</code> 配置类，如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="hl"><span class="lnt"> 5
</span></span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="hl"><span class="lnt">11
</span></span><span class="lnt">12
</span><span class="lnt">13
</span><span class="hl"><span class="lnt">14
</span></span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="hl"><span class="lnt">28
</span></span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="hl"><span class="lnt">34
</span></span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="hl"><span class="lnt">41
</span></span><span class="lnt">42
</span><span class="lnt">43
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Override</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">void</span> <span class="nf">registerBeanDefinitions</span><span class="o">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">AnnotationMetadata</span> <span class="n">importingClassMetadata</span><span class="o">,</span> <span class="n">BeanDefinitionRegistry</span> <span class="n">registry</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl">    <span class="n">AopConfigUtils</span><span class="o">.</span><span class="na">registerAspectJAnnotationAutoProxyCreatorIfNecessary</span><span class="o">(</span><span class="n">registry</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">AnnotationAttributes</span> <span class="n">enableAspectJAutoProxy</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">            <span class="n">AnnotationConfigUtils</span><span class="o">.</span><span class="na">attributesFor</span><span class="o">(</span><span class="n">importingClassMetadata</span><span class="o">,</span> <span class="n">EnableAspectJAutoProxy</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">(</span><span class="n">enableAspectJAutoProxy</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">enableAspectJAutoProxy</span><span class="o">.</span><span class="na">getBoolean</span><span class="o">(</span><span class="s">&#34;proxyTargetClass&#34;</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line hl"><span class="cl">            <span class="n">AopConfigUtils</span><span class="o">.</span><span class="na">forceAutoProxyCreatorToUseClassProxying</span><span class="o">(</span><span class="n">registry</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="n">enableAspectJAutoProxy</span><span class="o">.</span><span class="na">getBoolean</span><span class="o">(</span><span class="s">&#34;exposeProxy&#34;</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line hl"><span class="cl">            <span class="n">AopConfigUtils</span><span class="o">.</span><span class="na">forceAutoProxyCreatorToExposeProxy</span><span class="o">(</span><span class="n">registry</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@Nullable</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="n">BeanDefinition</span> <span class="nf">registerAspectJAnnotationAutoProxyCreatorIfNecessary</span><span class="o">(</span><span class="n">BeanDefinitionRegistry</span> <span class="n">registry</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">registerAspectJAnnotationAutoProxyCreatorIfNecessary</span><span class="o">(</span><span class="n">registry</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@Nullable</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="n">BeanDefinition</span> <span class="nf">registerAspectJAnnotationAutoProxyCreatorIfNecessary</span><span class="o">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">BeanDefinitionRegistry</span> <span class="n">registry</span><span class="o">,</span> <span class="nd">@Nullable</span> <span class="n">Object</span> <span class="n">source</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl">    <span class="k">return</span> <span class="n">registerOrEscalateApcAsRequired</span><span class="o">(</span><span class="n">AnnotationAwareAspectJAutoProxyCreator</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="n">registry</span><span class="o">,</span> <span class="n">source</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">forceAutoProxyCreatorToUseClassProxying</span><span class="o">(</span><span class="n">BeanDefinitionRegistry</span> <span class="n">registry</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">(</span><span class="n">registry</span><span class="o">.</span><span class="na">containsBeanDefinition</span><span class="o">(</span><span class="n">AUTO_PROXY_CREATOR_BEAN_NAME</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">BeanDefinition</span> <span class="n">definition</span> <span class="o">=</span> <span class="n">registry</span><span class="o">.</span><span class="na">getBeanDefinition</span><span class="o">(</span><span class="n">AUTO_PROXY_CREATOR_BEAN_NAME</span><span class="o">);</span>
</span></span><span class="line hl"><span class="cl">        <span class="n">definition</span><span class="o">.</span><span class="na">getPropertyValues</span><span class="o">().</span><span class="na">add</span><span class="o">(</span><span class="s">&#34;proxyTargetClass&#34;</span><span class="o">,</span> <span class="n">Boolean</span><span class="o">.</span><span class="na">TRUE</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">forceAutoProxyCreatorToExposeProxy</span><span class="o">(</span><span class="n">BeanDefinitionRegistry</span> <span class="n">registry</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">(</span><span class="n">registry</span><span class="o">.</span><span class="na">containsBeanDefinition</span><span class="o">(</span><span class="n">AUTO_PROXY_CREATOR_BEAN_NAME</span><span class="o">))</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">BeanDefinition</span> <span class="n">definition</span> <span class="o">=</span> <span class="n">registry</span><span class="o">.</span><span class="na">getBeanDefinition</span><span class="o">(</span><span class="n">AUTO_PROXY_CREATOR_BEAN_NAME</span><span class="o">);</span>
</span></span><span class="line hl"><span class="cl">        <span class="n">definition</span><span class="o">.</span><span class="na">getPropertyValues</span><span class="o">().</span><span class="na">add</span><span class="o">(</span><span class="s">&#34;exposeProxy&#34;</span><span class="o">,</span> <span class="n">Boolean</span><span class="o">.</span><span class="na">TRUE</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>重点部分已用高亮标出，主要是将 <code>AnnotationAwareAspectJAutoProxyCreator</code> 类作为 <code>BeanDefinition</code> 加入到容器中，且为其配置了 <code>proxyTargetClass</code> 属性和 <code>exposeProxy</code> 属性。<code>proxyTargetClass</code> 设置为 true，表示强制使用 CGLIB 动态代理；<code>exposeProxy</code> 设置为 true，表示需要暴露 <code>ManualA</code> 的「代理对象」，可以使用 <code>AopContext</code> 获取该对象。</p>
<p>自动创建的方式将 <code>Pointcut</code>、<code>Advice</code> 及 <code>Advisor</code> 交由 Spring 容器管理，容器启动后，会自动根据我们配置好的 <code>Advisor</code> 为我们将原始的 <code>ManualA</code> 对象进行增加，创建其「代理对象」，并将该代理对象放入 <code>singletonObjects</code> 缓存中。调用 <code>getBean</code> 可以从容器的 <code>singletonObjects</code> 缓存中获取出 <code>ManualA</code> 类型的 Bean，通过 <code>execute</code> 和 <code>other</code> 的执行结果我们可以发现该对象是「代理对象」而非原始的目标对象。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cn.liyangjie.spring.circular.ManualA<span class="nv">$$</span>EnhancerBySpringCGLIB<span class="nv">$$</span>d4361f08
</span></span><span class="line"><span class="cl">---------------------
</span></span><span class="line"><span class="cl">Enhancement Before...
</span></span><span class="line"><span class="cl">Executing A...
</span></span><span class="line"><span class="cl">Enhancement After...
</span></span><span class="line"><span class="cl">---------------------
</span></span><span class="line"><span class="cl">Executing A... in other<span class="o">()</span>
</span></span><span class="line"><span class="cl">Executing A...
</span></span><span class="line"><span class="cl">Other method...
</span></span></code></pre></td></tr></table>
</div>
</div><p>同时需要注意到，上述 <code>other</code> 方法内部调用的 <code>execute</code> 并没有经过增强，只是简单的调用原始方法，这就是 <code>exposeProxy</code> 的作用所在了，为了让 <code>other</code> 能够调用到增强的 <code>execute</code> 方法，这点也适用于 Spring 事务中，同一个类中的 <code>@Transactional</code> 方法间的相互调用。</p>
<p>现在将 <code>ManualA</code> 的 <code>other</code> 修改如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="hl"><span class="lnt">3
</span></span><span class="hl"><span class="lnt">4
</span></span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kt">void</span> <span class="nf">other</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Executing A... in other()&#34;</span><span class="o">);</span>
</span></span><span class="line hl"><span class="cl">    <span class="n">ManualA</span> <span class="n">proxy</span> <span class="o">=</span> <span class="o">(</span><span class="n">ManualA</span><span class="o">)</span> <span class="n">AopContext</span><span class="o">.</span><span class="na">currentProxy</span><span class="o">();</span>
</span></span><span class="line hl"><span class="cl">    <span class="n">proxy</span><span class="o">.</span><span class="na">execute</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Other method...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>再执行得到的结果如下：<code>other</code> 中成功调用了增强后的 <code>execute</code> 方法。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cn.liyangjie.spring.circular.ManualA<span class="nv">$$</span>EnhancerBySpringCGLIB<span class="nv">$$</span>d4361f08
</span></span><span class="line"><span class="cl">---------------------
</span></span><span class="line"><span class="cl">Enhancement Before...
</span></span><span class="line"><span class="cl">Executing A...
</span></span><span class="line"><span class="cl">Enhancement After...
</span></span><span class="line"><span class="cl">---------------------
</span></span><span class="line"><span class="cl">Executing A... in other<span class="o">()</span>
</span></span><span class="line"><span class="cl">---------------------
</span></span><span class="line"><span class="cl">Enhancement Before...
</span></span><span class="line"><span class="cl">Executing A...
</span></span><span class="line"><span class="cl">Enhancement After...
</span></span><span class="line"><span class="cl">---------------------
</span></span><span class="line"><span class="cl">Other method...
</span></span></code></pre></td></tr></table>
</div>
</div></li>
</ul>
<hr>
<h3 id="aop-下的循环依赖">AOP 下的循环依赖<a hidden class="anchor" aria-hidden="true" href="#aop-下的循环依赖">#</a></h3>
<p>现在我们使用示例代码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Component</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ServiceA</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">ServiceB</span> <span class="n">serviceB</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@Autowired</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setServiceB</span><span class="o">(</span><span class="n">ServiceB</span> <span class="n">serviceB</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="o">.</span><span class="na">serviceB</span> <span class="o">=</span> <span class="n">serviceB</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">service</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Executing Service A...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Component</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ServiceB</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="n">ServiceA</span> <span class="n">serviceA</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@Autowired</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setServiceA</span><span class="o">(</span><span class="n">ServiceA</span> <span class="n">serviceA</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">this</span><span class="o">.</span><span class="na">serviceA</span> <span class="o">=</span> <span class="n">serviceA</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">service</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Executing ServiceB...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@ComponentScan</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 使用 CGLIB 动态代理，并且暴露「代理对象」，可使用 AopContext 访问
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nd">@EnableAspectJAutoProxy</span><span class="o">(</span><span class="n">proxyTargetClass</span> <span class="o">=</span> <span class="kc">true</span><span class="o">,</span> <span class="n">exposeProxy</span> <span class="o">=</span> <span class="kc">true</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CircularReferenceDemo</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="kd">class</span> <span class="nc">CircularInterceptor</span> <span class="kd">implements</span> <span class="n">MethodInterceptor</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="nd">@Nullable</span>
</span></span><span class="line"><span class="cl">        <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="n">Object</span> <span class="nf">invoke</span><span class="o">(</span><span class="nd">@Nonnull</span> <span class="n">MethodInvocation</span> <span class="n">invocation</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Throwable</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&#34;Enhancement...&#34;</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@Bean</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">MethodInterceptor</span> <span class="nf">interceptor</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="n">CircularInterceptor</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@Bean</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Pointcut</span> <span class="nf">pointcut</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="n">StaticMethodMatcherPointcut</span><span class="o">(){</span>
</span></span><span class="line"><span class="cl">            <span class="nd">@Override</span>
</span></span><span class="line"><span class="cl">            <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">matches</span><span class="o">(</span><span class="n">Method</span> <span class="n">method</span><span class="o">,</span> <span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">targetClass</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="s">&#34;service&#34;</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">            <span class="o">}</span>
</span></span><span class="line"><span class="cl">        <span class="o">};</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@Bean</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="n">Advisor</span> <span class="nf">advisor</span><span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">new</span> <span class="n">DefaultPointcutAdvisor</span><span class="o">(</span><span class="n">pointcut</span><span class="o">(),</span> <span class="n">interceptor</span><span class="o">());</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 注解方式进行测试，需要使用 AnnotationConfigApplicationContext
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">AnnotationConfigApplicationContext</span> <span class="n">context</span> <span class="o">=</span> <span class="k">new</span> <span class="n">AnnotationConfigApplicationContext</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 注册当前 CircularReferenceDemo，以触发 @ComponentScan 扫描当前包
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">context</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="n">CircularReferenceDemo</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 启动容器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">context</span><span class="o">.</span><span class="na">refresh</span><span class="o">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 获取 bean 实例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">ServiceA</span> <span class="n">a</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="n">ServiceA</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">ServiceB</span> <span class="n">b</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="n">ServiceB</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>ServiceA</code> 和 <code>ServiceB</code> 存在循环依赖关系，且这两个对象都有 <code>service</code> 方法，满足 AOP 自动代理的条件，这就是典型的 AOP 场景下的循环依赖。</p>
<p>从之前的 AOP 示例中我们可以了解到，对于「代理对象」和原始目标对象，Spring 容器中对外暴露的是前者，因此，在依赖注入的过程中，循环依赖要注入的也是 <code>ServiceA</code> 和 <code>ServiceB</code> 的代理对象。</p>
<p>有了上面 setter 方式的分析，这里偷个懒，还是使用上面那张图，图中的 <code>A</code> 和 <code>B</code> 请读者自动替换为 <code>ServiceA</code> 和 <code>ServiceB</code>。在图中标出了 3 个紫色的位置，这是接下来要分析的重点。</p>
<p><figure><a class="lightgallery" href="https://s2.loli.net/2022/02/04/AWr2LFYve9l41Ey.png" title="https://s2.loli.net/2022/02/04/AWr2LFYve9l41Ey.png" data-thumbnail="https://s2.loli.net/2022/02/04/AWr2LFYve9l41Ey.png">
        <img
            class="lazy"
            src="/svg/loading.min.svg"
            data-src="https://s2.loli.net/2022/02/04/AWr2LFYve9l41Ey.png"
            loading="lazy"
            title="https://s2.loli.net/2022/02/04/AWr2LFYve9l41Ey.png"
            alt="https://s2.loli.net/2022/02/04/AWr2LFYve9l41Ey.png" />
    </a><figcaption class="image-caption"></figcaption>
</figure>
</p>
<p>流程还是和 setter 方式类似，从 <code>ServiceA</code> 进入，再到 <code>ServiceA</code> 中的 <code>populate</code> 触发 <code>ServiceB</code> 的获取。而 <code>ServiceB</code> 的 <code>populate</code> 中同样又需要 <code>ServiceA</code>，因此还是会进入到步骤 7。</p>
<p>如果有些记忆模糊的话，请回头去看看步骤 7 的具体流程，这里重新贴一下这段代码：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">protected</span> <span class="n">Object</span> <span class="nf">getEarlyBeanReference</span><span class="o">(</span><span class="n">String</span> <span class="n">beanName</span><span class="o">,</span> <span class="n">RootBeanDefinition</span> <span class="n">mbd</span><span class="o">,</span> <span class="n">Object</span> <span class="n">bean</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">     <span class="n">Object</span> <span class="n">exposedObject</span> <span class="o">=</span> <span class="n">bean</span><span class="o">;</span>
</span></span><span class="line"><span class="cl">     <span class="k">if</span> <span class="o">(!</span><span class="n">mbd</span><span class="o">.</span><span class="na">isSynthetic</span><span class="o">()</span> <span class="o">&amp;&amp;</span> <span class="n">hasInstantiationAwareBeanPostProcessors</span><span class="o">())</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">         <span class="k">for</span> <span class="o">(</span><span class="n">SmartInstantiationAwareBeanPostProcessor</span> <span class="n">bp</span> <span class="o">:</span> <span class="n">getBeanPostProcessorCache</span><span class="o">().</span><span class="na">smartInstantiationAware</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">             <span class="n">exposedObject</span> <span class="o">=</span> <span class="n">bp</span><span class="o">.</span><span class="na">getEarlyBeanReference</span><span class="o">(</span><span class="n">exposedObject</span><span class="o">,</span> <span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">         <span class="o">}</span>
</span></span><span class="line"><span class="cl">     <span class="o">}</span>
</span></span><span class="line"><span class="cl">     <span class="k">return</span> <span class="n">exposedObject</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"> <span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>刚才介绍过，当我们使用 <code>@EnableAspectJAutoProxy</code> 开启 AOP 自动创建模式后，容器中会导入一个 <code>AbstractAutoProxyCreator</code> 类，这个类此时满足这里的 <code>SmartInstantiationAwareBeanPostProcessor</code> 条件，因此会调用 <code>exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);</code> 对我们的原始 <code>ServiceA</code> 的半成品进行增强处理。<code>AbstractAutoProxyCreator</code> 中 <code>getEarlyBeanReference</code> 具体代码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Override</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">Object</span> <span class="nf">getEarlyBeanReference</span><span class="o">(</span><span class="n">Object</span> <span class="n">bean</span><span class="o">,</span> <span class="n">String</span> <span class="n">beanName</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Object</span> <span class="n">cacheKey</span> <span class="o">=</span> <span class="n">getCacheKey</span><span class="o">(</span><span class="n">bean</span><span class="o">.</span><span class="na">getClass</span><span class="o">(),</span> <span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">this</span><span class="o">.</span><span class="na">earlyProxyReferences</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">cacheKey</span><span class="o">,</span> <span class="n">bean</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapIfNecessary</span><span class="o">(</span><span class="n">bean</span><span class="o">,</span> <span class="n">beanName</span><span class="o">,</span> <span class="n">cacheKey</span><span class="o">);</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>这里就不深入展开了，只需要了解到 <code>wrapIfNecessary</code> 是具体创建半成品「代理对象」的位置就行了。</p>
<p>步骤 7 完成后，由于有了 <code>ServiceA</code> 半成品「代理对象」，此时就可以返回 <code>ServiceB</code> 的 <code>populate</code> 方法，并完成其属性填充工作。随后调用 <code>initializeBean</code> 方法，调用该方法前，<code>ServiceB</code> 还是原始的目标类型，没有任何增强。</p>
<p><code>initializeBean</code> 中在执行了各种初始化操作后，会进入 <code>BeanPostProcessor</code> 的 after 中，<code>AbstractAutoProxyCreator</code> 中该方法的实现如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Override</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="n">Object</span> <span class="nf">postProcessAfterInitialization</span><span class="o">(</span><span class="nd">@Nullable</span> <span class="n">Object</span> <span class="n">bean</span><span class="o">,</span> <span class="n">String</span> <span class="n">beanName</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">(</span><span class="n">bean</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Object</span> <span class="n">cacheKey</span> <span class="o">=</span> <span class="n">getCacheKey</span><span class="o">(</span><span class="n">bean</span><span class="o">.</span><span class="na">getClass</span><span class="o">(),</span> <span class="n">beanName</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">earlyProxyReferences</span><span class="o">.</span><span class="na">remove</span><span class="o">(</span><span class="n">cacheKey</span><span class="o">)</span> <span class="o">!=</span> <span class="n">bean</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">wrapIfNecessary</span><span class="o">(</span><span class="n">bean</span><span class="o">,</span> <span class="n">beanName</span><span class="o">,</span> <span class="n">cacheKey</span><span class="o">);</span>
</span></span><span class="line"><span class="cl">        <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">bean</span><span class="o">;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>是不是感觉非常得熟悉呢？这里同样调用了 <code>wrapIfNecessary</code> 方法，完成了 <code>ServiceB</code> 成品「代理对象」创建，注意，这里是成品。</p>
<p>接下来，使用这个 <code>ServiceB</code> 的成品「代理对象」完成 <code>ServiceA</code> 的 <code>populate</code> 工作，并进入到 <code>ServiceA</code> 的 <code>initializingBean</code> 方法。这时与 <code>ServiceB</code> 中不同的是，<code>ServiceA</code> 已经创建过一次并且进行了相关缓存（<code>cacheKey</code> 的作用），因此不会再进行创建，直接返回即可。</p>
<p>最终，容器中的 <code>ServiceA</code> 和 <code>ServiceB</code> 均顺利创建，都被进行了动态代理增强，且循环依赖也得到了解决。</p>
<hr>
<h2 id="总结">总结<a hidden class="anchor" aria-hidden="true" href="#总结">#</a></h2>
<p>Spring 循环依赖分为两种情况：</p>
<ul>
<li>构造器注入</li>
<li>setter注入</li>
</ul>
<p>只有在 setter（包括 <code>@Autowired</code> 注解，它的注入时期也是在 <code>populate</code> 中）注入情景下才能够解决，整体思路是利用提前「暴露」一个半成品对象完成其中一方的属性填充。而 AOP 场景下的循环依赖实际上还是依赖这套逻辑，只是对提前「暴露」的这个半成品进行了一些处理。</p>


  </div>

  <footer class="post-footer">
    <ul class="post-tags">
      <li><a href="https://www.liyangjie.cn/tags/java/">Java</a></li>
      <li><a href="https://www.liyangjie.cn/tags/spring/">Spring</a></li>
      <li><a href="https://www.liyangjie.cn/tags/%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96/">循环依赖</a></li>
    </ul><div id="post-licensing" class="admonition warning">
    <div class="details-summary admonition-title">
        版权声明 <a rel="license" target="_blank" href="https://creativecommons.org/licenses/by/4.0/deed.zh"><i class="fab fa-creative-commons"></i> <i class="fab fa-creative-commons-by"></i></a>
    </div>
    
    <div class="details-content">
        <div class="admonition-content">
            <ul>
                <li>本文作者：SadBird。</li>
                <li>本文链接：https://www.liyangjie.cn/posts/work/spring-circular-dependency/。</li>
                <li>许可说明：本站所有文章除特殊声明外，均使用 <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="license" target="_blank"><i class="fab fa-creative-commons"></i> <i class="fab fa-creative-commons-by"></i> CC BY 4.0 </a> 许可协议，转载请注明出处。</li>
            </ul>
        </div>
    </div>
</div>
<nav class="paginav">
  <a class="prev" href="https://www.liyangjie.cn/posts/work/hello-lua/">
    <span class="title"><i class="fas fa-angle-double-left"></i> Prev Page</span>
    <br>
    <span>Lua 脚本入门</span>
  </a>
  <a class="next" href="https://www.liyangjie.cn/posts/hobby/windows-activation/">
    <span class="title">Next Page <i class="fas fa-angle-double-right"></i></span>
    <br>
    <span>Windows 激活——MAS</span>
  </a>
</nav>

  </footer>
<footer class="tc-container" id="comment">
    <div class="tc-title"><p class="c-title">Discussion</p></div>
    <div id="tcomments"></div>
</footer>
<script crossorigin="anonymous" src="/js/twikoo.min.64322d6748f9b8b12dfb029616065f8eeed16467444adde58bab7d98c5733adf.js" integrity="sha256-ZDItZ0j5uLEt&#43;wKWFgZfju7RZGdESt3li6t9mMVzOt8="></script>
<script>
    twikoo.init({
        envId: 'https://twikoo-nu-red.vercel.app/',
        el: '#tcomments',
        region: 'ap-shenzhen', 
        
        lang: 'zh-CN', 
    });
</script>
</article>
    </main>
    
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
        <path d="M12 6H0l6-6z" />
    </svg>
</a><script>
    class Accordion {
        constructor(el) {
            
            this.el = el;
            
            this.summary = el.querySelector('summary');
            
            this.content = el.querySelector('.inner');

            
            this.animation = null;
            
            this.isClosing = false;
            
            this.isExpanding = false;
            
            this.summary.addEventListener('click', (e) => this.onClick(e));
        }

        onClick(e) {
            
            e.preventDefault();
            
            this.el.style.overflow = 'hidden';
            
            if (this.isClosing || !this.el.open) {
            this.open();
            
            } else if (this.isExpanding || this.el.open) {
            this.shrink();
            }
        }

        shrink() {
            
            this.isClosing = true;
            
            
            const startHeight = `${this.el.offsetHeight}px`;
            
            const endHeight = `${this.summary.offsetHeight}px`;
            
            
            if (this.animation) {
            
            this.animation.cancel();
            }
            
            
            this.animation = this.el.animate({
            
            height: [startHeight, endHeight]
            }, {
            duration: 400,
            easing: 'ease-out'
            });
            
            
            this.animation.onfinish = () => this.onAnimationFinish(false);
            
            this.animation.oncancel = () => this.isClosing = false;
        }

        open() {
            
            this.el.style.height = `${this.el.offsetHeight}px`;
            
            this.el.open = true;
            
            window.requestAnimationFrame(() => this.expand());
        }

        expand() {
            
            this.isExpanding = true;
            
            const startHeight = `${this.el.offsetHeight}px`;
            
            const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`;
            
            
            if (this.animation) {
            
            this.animation.cancel();
            }
            
            
            this.animation = this.el.animate({
            
            height: [startHeight, endHeight]
            }, {
            duration: 400,
            easing: 'ease-out'
            });
            
            this.animation.onfinish = () => this.onAnimationFinish(true);
            
            this.animation.oncancel = () => this.isExpanding = false;
        }

        onAnimationFinish(open) {
            
            this.el.open = open;
            
            this.animation = null;
            
            this.isClosing = false;
            this.isExpanding = false;
            
            this.el.style.height = this.el.style.overflow = '';
        }
    }

    document.querySelectorAll('.toc details').forEach((el) => {
        new Accordion(el);
    });
</script>
<a href="#comment" aria-label="go to bottom comment" title="Go to Bottom Comment (Alt + B)" class="top-link bottom-comment" id="bottom-comment" accesskey="b" style="visibility: visible; opacity: 1;">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 29.338 29.338" fill="currentcolor">
        <path d="M27.184,1.605H2.156C0.967,1.605,0,2.572,0,3.76v17.572c0,1.188,0.967,2.155,2.156,2.155h13.543
		l5.057,3.777c0.414,0.31,0.842,0.468,1.268,0.468c0.789,0,1.639-0.602,1.637-1.923v-2.322h3.523c1.188,0,2.154-0.967,2.154-2.155
		V3.76C29.338,2.572,28.371,1.605,27.184,1.605z M27.34,21.332c0,0.085-0.068,0.155-0.154,0.155h-5.523v3.955l-5.297-3.956H2.156
		c-0.086,0-0.154-0.07-0.154-0.155V3.759c0-0.085,0.068-0.155,0.154-0.155v0.001h25.029c0.086,0,0.154,0.07,0.154,0.155
		L27.34,21.332L27.34,21.332z M5.505,10.792h4.334v4.333H5.505C5.505,15.125,5.505,10.792,5.505,10.792z M12.505,10.792h4.334v4.333
		h-4.334V10.792z M19.505,10.792h4.334v4.333h-4.334V10.792z"/>
    </svg>
</a>

<script>
    let comment = document.getElementById("comment");
    let bottomToComment = document.getElementById("bottom-comment")
    
    document.addEventListener('scroll',function () {
        const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight 
        const offsetTop = comment.offsetTop
        const scrollTop = document.documentElement.scrollTop
        const top = offsetTop - scrollTop
        if (top <= viewPortHeight + 100) {
            bottomToComment.style.visibility = "hidden";
            bottomToComment.style.opacity = "0";
        } else {
            bottomToComment.style.visibility = "visible";
            bottomToComment.style.opacity = "1";
        }
    })
</script><footer class="footer">
    <span>&copy; 2022 <a href="https://www.liyangjie.cn/">染竹君的个人博客</a></span>
    <span>
        Powered by
        <a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
        <a href="https://git.io/hugopapermod" rel="noopener" target="_blank">PaperMod</a>
    </span>
    
</footer>

<script crossorigin="anonymous" src="/js/intersection-observer.min.c0ff09623e9f209a66c6130004e0422e1385fd8e8f8baaa14246a49c19827c68.js" integrity="sha256-wP8JYj6fIJpmxhMABOBCLhOF/Y6Pi6qhQkaknBmCfGg="></script>
<script crossorigin="anonymous" src="/js/lazyload.min.min.53e82bf9e8c145b953635b17fa3911ab6f3a8f1e4d37969aeb39d829d30fa6e9.js" integrity="sha256-U&#43;gr&#43;ejBRblTY1sX&#43;jkRq286jx5NN5aa6znYKdMPpuk="></script>


<script crossorigin="anonymous" src="/js/lightgallery.min.5d1410b8c831852c6c62b2dfad75f4e0f046a13e1826f97cd7a742523fab608c.js" integrity="sha256-XRQQuMgxhSxsYrLfrXX04PBGoT4YJvl816dCUj&#43;rYIw="></script>
<script crossorigin="anonymous" src="/js/lg-thumbnail.min.afa3995936244c14f68b85b13d657c368dc787cbcea863179950ac494dab6117.js" integrity="sha256-r6OZWTYkTBT2i4WxPWV8No3Hh8vOqGMXmVCsSU2rYRc="></script>
<script crossorigin="anonymous" src="/js/lg-zoom.min.a47b38d6f7138dce5f712f77780f339b5ec26af3bcd44950bcf736754ec88ed4.js" integrity="sha256-pHs41vcTjc5fcS93eA8zm17CavO81ElQvPc2dU7IjtQ="></script>
<script>
    const lazyLoadInstance = new LazyLoad({
        
    }); 
    
    const config = {
        selector: ".lightgallery",
        mode: "lg-slide",
        plugins: [lgZoom, lgThumbnail],
        speed: 400,
        hideBarsDelay: 2000,
        mousewheel: true,
        thumbnail: true,
        exThumbImage: "data-thumbnail",
        thumbWidth: 80,
        thumbContHeight: 80,
        mobileSettings: { controls: true, showCloseIcon: true, download: true, },
        licenseKey: "28AC9E09-3D8C45D8-8D6124E0-8FF74FF3", 
    };

    lightGallery(document.getElementsByClassName('post-content')[0], config);
</script>
<script>
    let details = document.getElementsByClassName('details')
    details = details || [];
    for (let i = 0; i < details.length; i++) {
        let element = details[i]
        const summary = element.getElementsByClassName('details-summary')[0];
        if (summary) {
            summary.addEventListener('click', () => {
                element.classList.toggle('open');
            }, false);
        }
    }
</script>

<script>
    let menu = document.getElementById('menu')
    if (menu) {
        menu.scrollLeft = localStorage.getItem("menu-scroll-position");
        menu.onscroll = function () {
            localStorage.setItem("menu-scroll-position", menu.scrollLeft);
        }
    }

    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener("click", function (e) {
            e.preventDefault();
            var id = this.getAttribute("href").substr(1);
            if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
                document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
                    behavior: "smooth"
                });
            } else {
                document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView();
            }
            if (id === "top") {
                history.replaceState(null, null, " ");
            } else {
                history.pushState(null, null, `#${id}`);
            }
        });
    });

</script>
<script>
    var mybutton = document.getElementById("top-link");
    window.onscroll = function () {
        if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
            mybutton.style.visibility = "visible";
            mybutton.style.opacity = "1";
        } else {
            mybutton.style.visibility = "hidden";
            mybutton.style.opacity = "0";
        }
    };

</script>
<script>
    document.getElementById("theme-toggle").addEventListener("click", () => {
        if (document.body.className.includes("dark")) {
            document.body.classList.remove('dark');
            localStorage.setItem("pref-theme", 'light');
        } else {
            document.body.classList.add('dark');
            localStorage.setItem("pref-theme", 'dark');
        }
    })

</script>
<script>
    document.querySelectorAll('pre > code').forEach((codeblock) => {
        const container = codeblock.parentNode.parentNode;

        const copybutton = document.createElement('button');
        copybutton.classList.add('copy-code');
        copybutton.innerHTML = 'copy';

        function copyingDone() {
            copybutton.innerHTML = 'copied!';
            setTimeout(() => {
                copybutton.innerHTML = 'copy';
            }, 2000);
        }

        copybutton.addEventListener('click', (cb) => {
            if ('clipboard' in navigator) {
                navigator.clipboard.writeText(codeblock.textContent);
                copyingDone();
                return;
            }

            const range = document.createRange();
            range.selectNodeContents(codeblock);
            const selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
            try {
                document.execCommand('copy');
                copyingDone();
            } catch (e) { };
            selection.removeRange(range);
        });

        if (container.classList.contains("highlight")) {
            container.appendChild(copybutton);
        } else if (container.parentNode.firstChild == container) {
            
        } else if (codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName == "TABLE") {
            
            codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(copybutton);
        } else {
            
            codeblock.parentNode.appendChild(copybutton);
        }
    });
</script>


<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [['$','$'], ['\\(','\\)']],
        processEscapes: true
      }
    });
</script>

<script type="text/javascript"
  src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script></body>

</html>